1. 程式人生 > 其它 >[ARC101C] Ribbons on Tree (容斥+DP)

[ARC101C] Ribbons on Tree (容斥+DP)

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;
}