题解 AT4352 【[ARC101C] Ribbons on Tree】

\[\huge\mathbb{DESCRIPTION} \]

编号:\(AT4352\)
算法:容斥原理、树形DP
时间复杂度:\(\mathcal O(N^2)\)

\[\huge\mathbb{SOL} \]

好难的容斥题目啊!
结合了树形DP!
我们来推一下式子...
\(F_X\)表示\(X\)中的边全部未被覆盖的方案数。
由容斥原理:

\[Ans=\sum_X(-1)^{|X|}\times F_X \]

考虑一下怎么推出\(F_X\)
如果把\(X\)中所有的边都去掉,那么每次选择加进来的点,都要在一个连通块内。
设剩下的连通块大小分别为\(Size_1,Size_2,Size_3...Size_K\)
\(Factorial_i\)表示\(i\)个点中两两一组的方案数。
\(i\)为奇数时\(Factorial_i=0\)
\(i\)为偶数时\(Factorial_i=(i-1)!!=(i-1)*(i-3)...*1\)
因此:

\[F_X=\prod Factorial_{Size_i} \]

那么如何快速地求出\(F_X\)
\(Dp[Now][i]\)表示子树\(Now\)中有\(i\)个点未匹配的方案数。
简单地转移一下即可,这里就不写了。

\[\huge\mathbb{CODE} \]

#include<bits/stdc++.h>
#define MOD 1000000007
#define MAX 5001
using namespace std;
int Total;
int Ans;
vector<int>Edge[MAX];
int Factorial[MAX];
int Dp[MAX][MAX];
int Size[MAX];
inline void Dfs(int Now,int Father)
{
	register int i;
	Size[Now]=1;
	Dp[Now][1]=1;
	for(i=0;i<Edge[Now].size();i++)
	{
		register int Next;
		Next=Edge[Now][i];
		if(Next!=Father)
		{
			Dfs(Next,Now);
			register int Tmp[MAX];
			register int j,k;
			for(j=1;j<=Size[Now]+Size[Next];j++)
			{
				Tmp[j]=0;
			}
			for(j=1;j<=Size[Now];j++)
			{
				for(k=1;k<=Size[Next];k++)
				{
					Tmp[j]=(Tmp[j]-(long long)Dp[Now][j]*Dp[Next][k]%MOD*Factorial[k])%MOD;
					Tmp[j+k]=(Tmp[j+k]+(long long)Dp[Now][j]*Dp[Next][k])%MOD;
				}
			}
			Size[Now]+=Size[Next];
			for(j=1;j<=Size[Now];j++)
			{
				Dp[Now][j]=Tmp[j];
			}
		}
	}
}
int main(void)
{
	register int i;
	cin>>Total;
	for(i=1;i<Total;i++)
	{
		register int U,V;
		cin>>U>>V;
		Edge[U].push_back(V);
		Edge[V].push_back(U);
	}
	Factorial[0]=1;
	for(i=2;i<=Total;i+=2)
	{
		Factorial[i]=(long long)Factorial[i-2]*(i-1)%MOD;
	}
	Dfs(1,0);
	for(i=2;i<=Total;i+=2)
	{
		Ans=(Ans+(long long)Dp[1][i]*Factorial[i])%MOD;
	}
	cout<<(Ans+MOD)%MOD<<endl;
	return 0;
}
posted @ 2020-08-13 10:31  Bushuai_Tang  阅读(152)  评论(0编辑  收藏  举报