P2767 树的数量 DP | 组合数学
题面:
给定\(n,m\)求\(n\)个节点的\(m\)叉树的形态有多少种
范围&性质:\(1\le n,m\le 127\)
分析:
- DP做法
当\(m\)等于2时就是卡特兰数,详情见卡特兰数定义和递推式
那我们考虑像Catlan数一样枚举每个儿子的大小然后组合起来,所以设\(f[i][j]\)表示表示根节点有\(i\)个\(m\)叉子树,总个数为\(j\)的方案数,\(g[i]\)表示用\(i\)个点构成一颗\(m\)叉树的方案数,转移时相当于往根节点上接一颗\(m\)叉树
\[f[i][j]=\sum g[k]\times f[i-1][j-k]
\]
为了消掉转移对原有状态的影响,还应该更新\(g\)数组
\[g[i]=f[m][i]
\]
复杂度\(O(n^3)\)
- 组合做法
我们将每个节点有几个儿子记录下来,按照编号会组成一个序列,序列里的每一项值的大小不会超过\(m\),序列一共有\(n\)项,每一个合法的序列一定满足元素之和等于\(n-1\),也就是说它的组合意义就是从\(n*m\)个点中选出\(n-1\)个的合法方案数,我们发现按照\(dfs\)序选儿子,每次能选的点会变少(因为已经被选过的点是不能选的)
所以对于所有情况合法的方案占总方案数的\(\prod_{i=1}^{n-1} \frac{i}{i+1}=\frac{1}{n}\)
最后总的答案就是\(ans=\frac{C(n*m,n-1)}{n}\)
代码:
#include<bits/stdc++.h>
using namespace std;
namespace zzc
{
const int mod = 10007;
long long n,m;
long long fac[mod+5],inv[mod+5];
void init()
{
fac[0]=fac[1]=1;
inv[0]=inv[1]=1;
for(int i=2;i<=mod;i++)
{
fac[i]=fac[i-1]*i%mod;
inv[i]=inv[mod%i]*(mod-mod/i)%mod;
}
for(int i=2;i<=mod;i++) inv[i]=inv[i]*inv[i-1]%mod;
}
long long qpow(long long x,long long y)
{
long long res=1;
while(y)
{
if(y&1) res=res*x%mod;
x=x*x%mod;
y>>=1;
}
return res;
}
long long C(long long n,long long m)
{
if(m>n) return 0;
return fac[n]*inv[n-m]%mod*inv[m]%mod;
}
long long lucas(long long n,long long m)
{
if(!m) return 1;
return lucas(n/mod,m/mod)*C(n%mod,m%mod)%mod;
}
void work()
{
init();
scanf("%lld%lld",&n,&m);
printf("%lld\n",lucas(n*m,n-1)*qpow(n,mod-2)%mod);
}
}
int main()
{
zzc::work();
return 0;
}