题目描述
题解
不是题解做法
生成树计数问题一般考虑统计重心,然后判掉两个重心的情况
设f[i][j]表示大小为i深度为j的个数,满足任何时候最长链<=L,然后容斥得到=L的答案
由于两个重心只有n为偶数时才可能,因此每次加入的子树大小不超过(n-1)/2,最后考虑偶数的情况
先加入深度为i-1的子树,按照大小顺序加入,之后每次按大小顺序加入深度<i-1的所有可能的子树,当顺序确定下来之后就不会算重
设同一大小当前可能深度的子树共有n种,要放m个这样大小的子树,那么方案数(无标号)为
(sum_{i=1}^{m}{C_n^i*C_{m-1}^{i-1}}=C_{n+m-1}^m)(范德蒙恒等式)
这个可以暴力算or递推,也可以预处理
因为有枚举子树大小和子树个数,这个是n ln n的,所以时间复杂度为O(n^3 ln n)
code
#include <bits/stdc++.h>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define min(a,b) (a<b?a:b)
#define ll long long
#define file
using namespace std;
ll f[201][201],g[201][201],F[201][201][201],G[201][201][201],w[201],ans;
int n,L,mod,i,j,k,l;
void dp(ll type)
{
int i,j,k,l;
ll Ans=0;
if (L<0) return;
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
memset(G,0,sizeof(G));
memset(F,0,sizeof(F));
fo(i,1,n)
f[0][1]=g[0][1]=F[0][1][i]=G[0][1][i]=1;
fo(i,1,L)
{
f[i][1]=1;
fd(k,(n-1)/2,1)
{
fd(j,n-k,1)
if (f[i][j])
{
if (i+i<=L)
{
fd(l,(n-j)/k,1)
f[i][j+k*l]=(f[i][j+k*l]+f[i][j]*F[i-1][k][l])%mod;
}
else
if (j==1)
f[i][j+k]=(f[i][j+k]+f[i-1][k])%mod;
}
}
f[i][1]=0;
if (i>1 && i<L)
{
fd(k,(n-1)/2,1)
{
fd(j,n-k,2)
if (f[i][j])
{
fd(l,(n-j)/k,1)
f[i][j+k*l]=(f[i][j+k*l]+f[i][j]*G[min(L-i-1,i-2)][k][l])%mod;
}
}
}
fo(j,1,n)
{
l=n/j;
g[i][j]=(g[i-1][j]+f[i][j])%mod;
F[i][j][1]=f[i][j];
G[i][j][1]=g[i][j];
fo(k,2,l)
{
F[i][j][k]=F[i][j][k-1]*(f[i][j]+k-1)%mod*w[k]%mod;
G[i][j][k]=G[i][j][k-1]*(g[i][j]+k-1)%mod*w[k]%mod;
}
}
}
Ans=g[L][n];
if (!(n&1))
{
fo(i,1,L-2)
Ans=(Ans+(i>1)*f[i][n/2]*G[min(L-i-1,i-1)][n/2][1]+(i+i+1<=L)*F[i][n/2][2])%mod;
}
ans=(ans+Ans*type)%mod;
}
int main()
{
freopen("exploit.in","r",stdin);
#ifdef file
freopen("exploit.out","w",stdout);
#endif
scanf("%d%d%d",&n,&L,&mod);
if (L==n)
{
printf("0
");
return 0;
}
w[1]=1;
fo(i,2,n)
w[i]=mod-w[mod%i]*(mod/i)%mod;
dp(1);
--L;
dp(-1);
printf("%lld
",(ans+mod)%mod);
fclose(stdin);
fclose(stdout);
return 0;
}