1. 程式人生 > >【CF917D】Stranger Trees 樹形DP+Prufer序列

【CF917D】Stranger Trees 樹形DP+Prufer序列

pri names brush 但是 連通 for limit limits cpp

【CF917D】Stranger Trees

題意:給你一棵n個點的樹,對於k=1...n,問你有多少有標號的n個點的樹,與給出的樹有恰好k條邊相同?

$n\le 100$

題解:我們先考慮容斥,求出和給出的樹至少有k個點相同的樹的數量。我們先選出原樹中的k條邊,然後剩下的邊隨便連。選出k條邊後,原樹被分成n-k個連通塊,設其大小分別為$siz_1,siz_2...siz_{n-k}$。那麽剩下的邊隨便連的方案數是多少呢?我們不妨把每個連通塊看成一個點,答案變成n個點的完全圖的生成樹個數,根據Prufer序列知道這個答案是$n^{n-2}$。但是這裏一個連通塊的大小並不是1。對於一個大小為$siz$的連通塊,如果它在Prufer序列中出現了j次,那麽它對答案的貢獻其實是$siz^{j+1}$(因為它的度數是j+1)。我們可以先把$\prod\limits_{i=1}^{n-k}siz_i$提出來,然後對於Prufer序列中的每個位置,如果它是第i個連通塊,則貢獻為$siz_i$,所以總的貢獻為$\sum\limits_{i=1}^{n-k}siz_i=n$,那麽答案就是$\prod\limits_{i=1}^{n-k}siz_i\times n^{n-k-2}$。

所以我們考慮樹形DP,用f[x][a][b]表示在x的子樹中,已經連了a條邊,包含x的連通塊大小為b的總貢獻。最後容斥一發即可。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;
const ll P=1000000007;
int n,m,cnt;
int to[210],nxt[210],head[110],siz[110];
ll f[110][110][110],g[110][110],c[110][110],h[110],bt[110];
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)	f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+gc-‘0‘,gc=getchar();
	return ret*f;
}
void dfs(int x,int fa)
{
	f[x][1][0]=1,siz[x]=1;
	for(int i=head[x],j,k,a,b,y;i!=-1;i=nxt[i])	if(to[i]!=fa)
	{
		y=to[i],dfs(to[i],x);
		memset(g,0,sizeof(g));
		for(j=1;j<=siz[x];j++)	for(k=1;k<=siz[y];k++)
		{
			for(a=0;a<siz[x];a++)	for(b=0;b<siz[y];b++)
			{
				g[j+k][a+b+1]=(g[j+k][a+b+1]+f[x][j][a]*f[y][k][b])%P;
				g[j][a+b]=(g[j][a+b]+f[x][j][a]*f[y][k][b]%P*k)%P;
			}
		}
		memcpy(f[x],g,sizeof(g));
		siz[x]+=siz[y];
	}
}
inline void add(int a,int b)
{
	to[cnt]=b,nxt[cnt]=head[a],head[a]=cnt++;
}
int main()
{
	n=rd();
	int i,j,a,b;
	memset(head,-1,sizeof(head));
	for(i=1;i<n;i++)	a=rd(),b=rd(),add(a,b),add(b,a);
	dfs(1,0);
	for(bt[0]=i=1;i<=n;i++)	bt[i]=bt[i-1]*n%P;
	for(i=0;i<=n;i++)	for(c[i][0]=j=1;j<=i;j++)	c[i][j]=(c[i-1][j-1]+c[i-1][j])%P;
	for(i=1;i<n;i++)	for(j=0;j<n;j++)	h[j]=(h[j]+f[1][i][j]*i)%P;
	h[n-1]=1;
	for(i=0;i<n-1;i++)	h[i]=h[i]*bt[n-i-2]%P;
	for(i=n-1;i>=0;i--)
	{
		for(j=i+1;j<n;j++)	h[i]=(h[i]-c[j][i]*h[j])%P;
		h[i]=(h[i]+P)%P;
	}
	for(i=0;i<n;i++)	printf("%lld ",h[i]);
	return 0;
}

【CF917D】Stranger Trees 樹形DP+Prufer序列