[atARC101E]Ribbons on Tree
令$f(E')$表示强制$E'$中的边不被覆盖的方案数,根据容斥,$ans=\sum_{E'\subseteq E}(-1)^{|E'|}f(E')$
对于给定的$E'$,$f(E')$即将$E'$中所有边删除,连通块内部的匹配方案数乘积:若连通块大小为奇数,则必然为0;若连通块大小为偶数,设为$2n$,则方案数为$\frac{(2n)!}{n!2^{n}}$(以下记为$g(2n)$)
考虑dp来计算,令$f[i][j]$表示以$i$为根的子树中,与$i$相连的连通块大小为$j$的方案数(方案数不仅指删边,还需要将已经独立的连通块配对,且带有符号)
转移对$i$到子树内的边是否删除分类讨论:1.不删除,$f[i][x+y]=\sum f[i][x]\cdot f[son][y]$;2.删除,还需要变号,$f[i][x]*=-\sum g(y,0)f[son][y]$,最终答案即$\sum_{i=0}^{n}g(i)\cdot f[1][i]$
每一个点向上转移复杂度不超过为$(sz[fa]-sz[k])sz[k]$,累加起来即$sz[1]^{2}$,总时间复杂度为$o(n^{2})$
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 5005 4 #define mod 1000000007 5 struct ji{ 6 int nex,to; 7 }edge[N<<1]; 8 int E,n,x,y,ans,head[N],sz[N],g[N],ff[N],f[N][N]; 9 void add(int x,int y){ 10 edge[E].nex=head[x]; 11 edge[E].to=y; 12 head[x]=E++; 13 } 14 void dfs(int k,int fa){ 15 sz[k]=f[k][1]=1; 16 for(int i=head[k];i!=-1;i=edge[i].nex) 17 if (edge[i].to!=fa){ 18 dfs(edge[i].to,k); 19 for(int j=1;j<=sz[k];j++)ff[j]=f[k][j]; 20 for(int j=1;j<=sz[k];j++)f[k][j]=0; 21 for(int j=1;j<=sz[k];j++) 22 for(int t=1;t<=sz[edge[i].to];t++){ 23 f[k][j+t]=(f[k][j+t]+1LL*ff[j]*f[edge[i].to][t])%mod; 24 f[k][j]=(f[k][j]+mod-1LL*g[t]*f[edge[i].to][t]%mod*ff[j]%mod)%mod; 25 } 26 sz[k]+=sz[edge[i].to]; 27 } 28 } 29 int main(){ 30 g[0]=1; 31 for(int i=1;i<N-4;i++) 32 if (i&1)g[i]=0; 33 else g[i]=1LL*g[i-2]*(i-1)%mod; 34 scanf("%d",&n); 35 memset(head,-1,sizeof(head)); 36 for(int i=1;i<n;i++){ 37 scanf("%d%d",&x,&y); 38 add(x,y); 39 add(y,x); 40 } 41 dfs(1,0); 42 for(int i=1;i<=n;i++)ans=(ans+1LL*g[i]*f[1][i])%mod; 43 printf("%d",ans); 44 }