CF724F 题解

原题链接

题目大意

如果两棵树可以通过重标号后变为完全相同,那么它们就是同构的。
将中间节点定义为度数大于\(1\)的节点。
计算由\(n\)个节点,其中所有的中间节点度数都为\(d\)的互不同构的树的数量。
答案对大质数取模。

\(Solution\)

解决问题的重点在于如何判断树是否同构,然鹅由于无根树是可以重标号(换根)的,所以我们需要对于每棵树找出一个特殊的根,将无根树转化为有根树

我们不妨将重心视为一棵树的根,而重心有一个至关重要的性质:任意字数大小不超过\(n/2\)(但是对于有两个重心的情况我们在后面加以讨论)

在确定了根以后,我们考虑如何统计方案数

因为这是dp专题里面的题,我们令\(f_{i,j,k}\)表示一棵树有\(i\)个节点,\(j\)棵子树(那么此子树的根的度数即为\(j+1\)),每棵子树节点数都不超过\(k\)的有根树数量

若所有子树的大小均\(\leq k\),显然有\(f_{i,j,k}\)+=\(f_{i,j,k-1}\)

那么剩下的情况即为所有子树的大小均为\(k\),这时出现了一个问题:

设当前树的根为\(u\),那么对于它的两棵子树\(son_1,son_2\),若两棵子树的\(size\)相同(均为\(k\)),此时\(son_1\)选择了方案\(f_1\)\(son_2\)选择了方案\(f_2\),然后\(son_1\)选择了方案\(f_2\)\(son_2\)选择了方案\(f_1\)时,由于两棵子树\(size\)相同,此时就出现了两棵同构的树,所以我们必须枚举大小为\(k\)的子树个数,由于上一种情况中\(f_{i,j,k-1}\)是一个已经考虑过的子问题,现在的问题被我们转化为:你有\(t\)\(size\)均为\(k\)的子树,求将这些子树组合成一棵大树(?)的方案数,子树间互不区分

那么我们\(\sum\limits_{t=1}\)来枚举有多少\(size\)均为\(k\)的子树,那么有:

\(f_{i,j,k}\)+=\(\sum\limits_{t=1}^{}{f_{i-tk,j-1,k-1} \times { {f_{k,d-1,k-1}+t-1} \choose t}}(tk \leq i , t \leq k)\)

其中\(x+t-1 \choose t\)表示在\(x\)种方案中不分顺序地选取\(t\)种,可重复的方案数。

有了\(dp\)的式子后,我们来统计答案:

回顾一下,我们现在还有双重心的情况没有研究,显然,双重心存在当且仅当\(2|n\)

那么若\(n\)为奇数,答案即为\(f_{n,d,\left\lfloor\frac{n}{2}\right\rfloor}\)

\(n\)为偶数,还要减掉双重心的情况(在上面被算了两次),双重心的情况个数即为\(f_{\frac{n}{2},d-1,\frac{n}{2}} \choose 2\)

\(Code:\)

#include<bits/stdc++.h>
using namespace std;
namespace my_std
{
	typedef long long ll;
	typedef double db;
	#define pf printf
	#define pc putchar
	#define fr(i,x,y) for(register ll i=(x);i<=(y);i++)
	#define pfr(i,x,y) for(register ll i=(x);i>=(y);i--)
	#define go(x) for(ll i=head[u];i;i=e[i].nxt)
	#define enter pc('\n')
	#define space pc(' ')
	#define fir first
	#define sec second
	#define MP make_pair
	const ll inf=0x3f3f3f3f;
	const ll inff=1e15;
	inline ll read()
	{
		ll sum=0,f=1;
		char ch=0;
		while(!isdigit(ch))
		{
			if(ch=='-') f=-1;
			ch=getchar();
		}
		while(isdigit(ch))
		{
			sum=(sum<<1)+(sum<<3)+(ch^48);
			ch=getchar();
		}
		return sum*f;
	}
	inline void write(ll x)
	{
		if(x<0)
		{
			x=-x;
			pc('-');
		}
		if(x>9) write(x/10);
		pc(x%10+'0');
	}
	inline void writeln(ll x)
	{
		write(x);
		enter;
	}
	inline void writesp(ll x)
	{
		write(x);
		space;
	}
}
using namespace my_std;
const ll N=1050;
ll n,d,mod,f[N][20][N],inv[20],mul[20]; 
inline ll ksmod(ll a,ll b)
{
	ll ans=1;
	while(b)
	{
		if(b&1)
		{
			ans=(ans*a)%mod;
		}
		a=(a*a)%mod;
		b>>=1;
	}
	return ans;
}
inline ll C(ll n,ll m,ll ans=1)
{
	if(m>n||m<0||n<0) return 0;
	fr(i,1,m) ans=ans*(n-i+1)%mod;
    ans=ans*inv[m]%mod;
    return ans;
}
inline void init()
{
	mul[0]=inv[0]=1;
    for(ll i=1;i<=10;i++) mul[i]=(mul[i-1]*i)%mod;
    inv[10]=ksmod(mul[10],mod-2);
    for(ll i=10-1;i;i--) inv[i]=inv[i+1]*(i+1)%mod;
}
int main(void)
{
	n=read(),d=read(),mod=read();
	if(n==1||n==2)
	{
		puts("1");
		return 0;
	}
	init();
	fr(i,0,n) f[1][0][i]=1;
	fr(i,2,n) for(ll j=1;j<i&&j<=d;j++) fr(k,1,n)
	{
		f[i][j][k]=f[i][j][k-1];
		for(ll t=1;t*k<i&&t<=j;t++)
		{
			if(k!=1) f[i][j][k]=(f[i][j][k]+f[i-t*k][j-t][k-1]*C(f[k][d-1][k-1]+t-1,t)%mod)%mod;
			else f[i][j][k]=(f[i][j][k]+f[i-t*k][j-t][k-1]*C(f[k][0][k-1]+t-1,t)%mod)%mod;
		} 
	}
	ll ans=f[n][d][n/2];
	if(!(n&1)) ans=(ans-C(f[n/2][d-1][n/2],2)+mod)%mod;
	writeln(ans);
	return 0;
}
posted @ 2020-04-03 19:20  L_G_J  阅读(121)  评论(1编辑  收藏  举报