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;
}
posted @ 2020-10-15 22:14  youth518  阅读(96)  评论(0编辑  收藏  举报