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