BZOJ4787/UOJ290 【ZJOI2017】仙人掌


本文版權歸ljh2000和博客園共有,歡迎轉載,但須保留此聲明,並給出原文鏈接,謝謝合作。

 

本文作者:ljh2000 
作者博客:http://www.cnblogs.com/ljh2000-jump/
轉載請注明出處,侵權必究,保留最終解釋權!

 

題目鏈接:UOJ290

正解:$DP$+仙人掌

解題報告:

  考慮環上的邊,不可能在連邊中再被覆蓋,所以只需要考慮樹邊就好了。

  把環拆掉,只剩下若干棵樹,就是一個森林,最后把每棵樹的答案用乘法原理合並起來就好了。

    對於每個節點$u$,我們考慮他的子樹的連邊方案數如何統計。

    如果我們強制每個結點的子樹必須向外連一條邊(顯然最多一條),往上統計的話,

  那么假設$u$的子樹內沒有向外連邊,那么就是把兒子節點的$ans$乘起來。

  如果向外連邊了,就需要考慮互相連邊的合法情況有多少種了。我們發現這個方案數只和兒子節點個數有關,可以很容易的用遞推式來表示:

  $g[n]=g[n-1]+g[n-2]*(n-1)$

    預處理出$g$數組,每次對於每個節點先把兒子節點的$ans$全乘進來,接下來需要分類討論節點$u$是不是一棵樹的根。

  如果是根的話,則不能向外連邊,那么再乘上兒子節點個數的$g$就好了(相當於是組合了節點個數個點的互相連邊方式);

  否則,可以向外連邊,那么把節點$u$本身也可以算進來,就是再乘上兒子節點個數$+1$的$g$。

 

//It is made by ljh2000
//有志者,事竟成,破釜沉舟,百二秦關終屬楚;苦心人,天不負,卧薪嘗膽,三千越甲可吞吳。
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <string>
#include <complex>
#include <bitset>
using namespace std;
typedef long long LL;
typedef long double LB;
typedef complex<double> C;
const double pi = acos(-1);
const int MAXN = 500011;
const int MAXM = 1000011; 
const int mod = 998244353;
int n,m,ecnt,first[MAXN],to[MAXM],next[MAXM],father[MAXN],lu[MAXN],dfn[MAXN],deep[MAXN];
LL g[MAXN],f[MAXN],ans;
struct node{ int id,x; }a[MAXN];
inline bool cmp(node q,node qq){ return q.x<qq.x; }
inline void link(int x,int y){ next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; }
inline int getint(){
    int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
    if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
}

inline void dfs(int x,int fa){
	dfn[x]=++ecnt;
	for(int i=first[x];i;i=next[i]) {
		int v=to[i]; if(v==fa) continue;
		if(!dfn[v]) father[v]=x,deep[v]=deep[x]+1,dfs(v,x);
	}
}

inline void dp(int x,bool rt){
	lu[x]=-1; f[x]=1; int tot=0;//the number of son
	for(int i=first[x];i;i=next[i]) {
		int v=to[i]; if(v==father[x]) continue;
		if(lu[v]!=1) continue;
		tot++;
		dp(v,0); f[x]*=f[v]; f[x]%=mod;
	}
	if(tot==0) return ;
	if(!rt) f[x]*=g[tot+1],f[x]%=mod;
	else f[x]*=g[tot],f[x]%=mod;
}
 
inline void work(){
	g[0]=g[1]=1; for(int i=2;i<=500001;i++) g[i]=g[i-1]+g[i-2]*(i-1),g[i]%=mod;
	int T=getint(); int x,y; bool ck;
	while(T--) {
		n=getint(); m=getint(); ecnt=1;
		for(int i=1;i<=n;i++) first[i]=father[i]=dfn[i]=deep[i]=lu[i]=0;
		for(int i=1;i<=m;i++) {
			x=getint(); y=getint();
			link(x,y); link(y,x);
		}
		ecnt=0; deep[1]=1; dfs(1,0); ck=true;
		for(int i=1;i<=m;i++) {//統計lu數組(到根的路徑條數),判斷是否為仙人掌
			x=to[i<<1]; y=to[i<<1|1]; if(dfn[x]<dfn[y]) swap(x,y);
			while(x!=y) {
				lu[x]++;
				if(lu[x]>2) { ck=false; break; }
				x=father[x];
			}
		}
		if(!ck) { printf("0\n"); continue; }
		for(int i=1;i<=n;i++) a[i].id=i,a[i].x=deep[i];
		sort(a+1,a+n+1,cmp);
		ans=1;
		for(int i=1;i<=n;i++) {
			x=a[i].id;
			if(lu[x]!=-1) {
				dp(x,1);
				ans*=f[x]; ans%=mod;
			}
		}
		printf("%lld\n",ans);
	}
}
 
int main()
{
    work();
    return 0;
}
//有志者,事竟成,破釜沉舟,百二秦關終屬楚;苦心人,天不負,卧薪嘗膽,三千越甲可吞吳。

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM