[ARC101C] Ribbons on Tree (容斥+DP)
阿新 • • 發佈:2022-02-22
AT4352 [ARC101C] Ribbons on Tree
妙題,如果按照套路子樹 DP 匹配子樹內外的點 \(O(n^3)\)
但是劍走偏鋒容斥一下,欽定若干條邊一定不被覆蓋,發現問題變成了若干個聯通塊任意配對方案數乘積
而一個大小為 \(n\) (\(n\) 為偶數)聯通塊任意匹配方案數為
\[g_n=\prod_{i=1}^{\frac{n}{2}} (2i-1) \]考慮用 DP 優化容斥,轉移過程中維護容斥係數,\(f_{u,i}\) 表示 \(u\) 目前所在聯通塊大小為 \(i\) ,暴力捲起來轉移即可
#include <cstdio> #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<22,stdin)),p1==p2?EOF:*p1++) #pragma GCC optimize(2,3,"Ofast") using namespace std; char buf[1<<22],*p1=buf,*p2=buf; int read(){ char c=getchar();int x=0; while(c<48||c>57) c=getchar(); do x=(x<<1)+(x<<3)+(c^48),c=getchar(); while(c>=48&&c<=57); return x; } const int _=5003; const int P=1000000007; int hd[_],ver[_<<1],nxt[_<<1],tot; void add(int u,int v){nxt[++tot]=hd[u];hd[u]=tot;ver[tot]=v;} int f[_][_],g[_],sz[_],t[_],n; void dfs(int u,int fa){ sz[u]=1;f[u][1]=1; for(int i=hd[u],v;i;i=nxt[i]) if((v=ver[i])^fa){ dfs(v,u); for(int j=0;j<=sz[u];++j) t[j]=f[u][j],f[u][j]=0; for(int j=1;j<=sz[u];++j) for(int k=0;k<=sz[v];++k) f[u][j+k]=(f[u][j+k]+1ll*t[j]*f[v][k])%P; sz[u]+=sz[v]; } for(int i=2;i<=sz[u];i+=2) f[u][0]=(f[u][0]-1ll*g[i]*f[u][i]%P+P)%P; } int main(){ n=read(); for(int i=1;i<n;++i){ int u=read(),v=read(); add(u,v);add(v,u); } g[0]=1;for(int i=2;i<=n;i+=2) g[i]=1ll*g[i-2]*(i-1)%P; dfs(1,0); printf("%d\n",P-f[1][0]); return 0; }