题面:
给定(n,m)求(n)个节点的(m)叉树的形态有多少种
范围&性质:(1le n,mle 127)
分析:
- DP做法
当(m)等于2时就是卡特兰数,详情见卡特兰数定义和递推式
那我们考虑像Catlan数一样枚举每个儿子的大小然后组合起来,所以设(f[i][j])表示表示根节点有(i)个(m)叉子树,总个数为(j)的方案数,(g[i])表示用(i)个点构成一颗(m)叉树的方案数,转移时相当于往根节点上接一颗(m)叉树
[f[i][j]=sum g[k] imes 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
",lucas(n*m,n-1)*qpow(n,mod-2)%mod);
}
}
int main()
{
zzc::work();
return 0;
}