Description
Input
Output
Sample Input
3 2
1 2
1 3
5 4
1 2
2 3
2 4
1 5
Sample Output
2
8
對於第一組樣例合法加邊的方案有 {}, {(2,3)},共 2 種。
正解:仙人掌$DP$
這題好難啊。。我看題解都看了好久才看懂。。
先給兩個博客:http://blog.csdn.net/akak__ii/article/details/65935711
ljh2000:http://www.cnblogs.com/ljh2000-jump/p/6613829.html
首先特判不是仙人掌的情況,只要判每個點到達根的路徑是否大於$2$條就行了。
然后我們可以先把環拆掉,也就是把環邊和對應的那個點與它父親斷開,因為環是不會對答案造成貢獻的。然后這個仙人掌就會變成一個森林。於是我們就成功地把仙人掌$DP$變成了樹形$DP$。我們單獨考慮每棵樹的答案,乘法原理一下就好。
然后就是對於每棵樹統計答案了。
對於一個點$x$,我們設$f[x]$表示$x$這棵子樹連邊形成仙人掌的方案數。我們發現,可以分為兩種情況:
1,$x$這棵子樹一定不與祖先連邊,這個是根的情況。
2,$x$這棵子樹可能與祖先連邊,這個是除了根以外其他點的情況。
對於第1種情況,我們把$x$所有的兒子$f[v]$都乘起來,並且我們計算一下$x$的兒子互相連邊的情況,再乘起來就行了。
對於$x$的兒子互相連邊的情況,我們可以找到一個規律。我們設$g[i]$表示$i$個兒子互相連邊的合法方案數,那么$g[i]=g[i-1]+(i-1)*g[i-2]$。
這是怎么來的呢?我們考慮一下,如果第$i$個點不與其他點連邊,那么方案數就是$g[i-1]$,否則,第$i$個點與第$j$個點連邊,那么第$j$個點肯定不能與其他點連邊,所以方案數是$g[i-2]$,總共有$i-1$種情況,所以$g[i]=g[i-1]+(i-1)*g[i-2]$。那么我們設$x$有$tot$個兒子,於是$f[x]=\prod f[v]*g[tot]$。
那么現在我們只要考慮第二種情況了。其實仔細想想,就是$x$的所有兒子$f[v]$相乘,再乘上$g[tot+1]$就行了。因為這就是$tot+1$個點互相連邊的情況。於是$f[x]=\prod f[v]*g[tot+1]$。
於是這道題我們就完美地解決了。
1 //It is made by wfj_2048~ 2 #include <algorithm> 3 #include <iostream> 4 #include <complex> 5 #include <cstring> 6 #include <cstdlib> 7 #include <cstdio> 8 #include <vector> 9 #include <cmath> 10 #include <queue> 11 #include <stack> 12 #include <map> 13 #include <set> 14 #define rhl (998244353) 15 #define inf (1<<30) 16 #define M (1000010) 17 #define N (500010) 18 #define il inline 19 #define RG register 20 #define ll long long 21 #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) 22 23 using namespace std; 24 25 struct edge{ int nt,to; }G[2*M]; 26 struct node{ int i,d; }a[N]; 27 28 int head[N],fa[N],dfn[N],dep[N],lu[N],n,m,cnt; 29 ll f[N],g[N],ans; 30 31 il int gi(){ 32 RG int x=0,q=1; RG char ch=getchar(); 33 while ((ch<'0' || ch>'9') && ch!='-') ch=getchar(); 34 if (ch=='-') q=-1,ch=getchar(); 35 while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar(); 36 return q*x; 37 } 38 39 il void insert(RG int from,RG int to){ 40 G[++cnt]=(edge){head[from],to},head[from]=cnt; return; 41 } 42 43 il int cmpd(const node &a,const node &b){ return a.d<b.d; } 44 45 il void pre(){ //預處理g數組 46 g[0]=g[1]=1; 47 for (RG int i=2;i<=500000;++i) g[i]=(g[i-1]+(i-1)*g[i-2])%rhl; 48 return; 49 } 50 51 il void dfs(RG int x,RG int p){ 52 fa[x]=p,dfn[x]=++cnt,dep[x]=dep[p]+1; 53 for (RG int i=head[x],v;i;i=G[i].nt){ 54 v=G[i].to; if (dfn[v]) continue; 55 dfs(v,x); 56 } 57 return; 58 } 59 60 il void dp(RG int x,RG int rt){ 61 lu[x]=-1,f[x]=1; RG int tot=0,v; 62 for (RG int i=head[x];i;i=G[i].nt){ 63 v=G[i].to; if (v==fa[x] || lu[v]!=1) continue; 64 tot++; dp(v,0); f[x]=f[x]*f[v]%rhl; 65 } 66 if (!rt) f[x]=f[x]*g[tot+1]%rhl; 67 else f[x]=f[x]*g[tot]%rhl; 68 return; 69 } 70 71 il void work(){ 72 n=gi(),m=gi(),cnt=1; 73 for (RG int i=1;i<=n;++i) lu[i]=fa[i]=dep[i]=dfn[i]=head[i]=0; 74 for (RG int i=1,u,v;i<=m;++i) u=gi(),v=gi(),insert(u,v),insert(v,u); 75 cnt=0; dfs(1,0); 76 for (RG int i=1,u,v;i<=m;++i){ //統計每個點到根的路徑數 77 u=G[i<<1].to,v=G[i<<1|1].to; 78 if (dfn[u]<dfn[v]) swap(u,v); 79 while (u!=v){ 80 if (lu[u]==2){ printf("0\n"); return; } 81 lu[u]++,u=fa[u]; 82 } 83 } 84 for (RG int i=1;i<=n;++i) a[i].i=i,a[i].d=dep[i]; 85 sort(a+1,a+n+1,cmpd); ans=1; 86 for (RG int i=1,x;i<=n;++i){ 87 x=a[i].i; if (lu[x]==-1) continue; 88 dp(x,1); ans=ans*f[x]%rhl; 89 } 90 printf("%lld\n",ans); return; 91 } 92 93 int main(){ 94 File("cactus"); 95 pre(); RG int T=gi(); 96 while (T--) work(); 97 return 0; 98 }