[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})$

 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 } 
View Code
原文地址:https://www.cnblogs.com/PYWBKTDA/p/13953969.html