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)的子树,求将这些子树组合成一棵大树(?)的方案数,子树间互不区分

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

(f_{i,j,k})+=(sumlimits_{t=1}^{}{f_{i-tk,j-1,k-1} imes { {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,leftlfloorfrac{n}{2} ight floor})

(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('
')
	#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;
}
原文地址:https://www.cnblogs.com/lgj-lgj/p/12628474.html