本文版權歸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; } //有志者,事竟成,破釜沉舟,百二秦關終屬楚;苦心人,天不負,卧薪嘗膽,三千越甲可吞吳。