P2767 树的数量 DP | 组合数学

题面:

给定 n , m n,m n,m n n n个节点的 m m m叉树的形态有多少种

范围&性质: 1 ≤ n , m ≤ 127 1\le n,m\le 127 1n,m127

分析:

  • DP做法

m m m等于2时就是卡特兰数,详情见卡特兰数定义和递推式

那我们考虑像Catlan数一样枚举每个儿子的大小然后组合起来,所以设 f [ i ] [ j ] f[i][j] f[i][j]表示表示根节点有 i i i m m m叉子树,总个数为 j j j的方案数, g [ i ] g[i] g[i]表示用 i i i个点构成一颗 m m m叉树的方案数,转移时相当于往根节点上接一颗 m m m叉树

f [ i ] [ j ] = ∑ g [ k ] × f [ i − 1 ] [ j − k ] f[i][j]=\sum g[k]\times f[i-1][j-k] f[i][j]=g[k]×f[i1][jk]

为了消掉转移对原有状态的影响,还应该更新 g g g数组

g [ i ] = f [ m ] [ i ] g[i]=f[m][i] g[i]=f[m][i]

复杂度 O ( n 3 ) O(n^3) O(n3)

  • 组合做法

我们将每个节点有几个儿子记录下来,按照编号会组成一个序列,序列里的每一项值的大小不会超过 m m m,序列一共有 n n n项,每一个合法的序列一定满足元素之和等于 n − 1 n-1 n1,也就是说它的组合意义就是从 n ∗ m n*m nm个点中选出 n − 1 n-1 n1个的合法方案数,我们发现按照 d f s dfs dfs序选儿子,每次能选的点会变少(因为已经被选过的点是不能选的)

所以对于所有情况合法的方案占总方案数的 ∏ i = 1 n − 1 i i + 1 = 1 n \prod_{i=1}^{n-1} \frac{i}{i+1}=\frac{1}{n} i=1n1i+1i=n1

最后总的答案就是 a n s = C ( n ∗ m , n − 1 ) n ans=\frac{C(n*m,n-1)}{n} ans=nC(nm,n1)

代码:

#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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
形动态规划(Tree DP)是一种常用的动态规划算法,用于解决结构相关的问题。在Python中,可以使用递归或者迭代的方式实现DPDP的基本思想是,从的叶子节点开始,逐层向上计算每个节点的状态,并利用已经计算过的节点状态来更新当前节点的状态。这样可以通过自底向上的方式,逐步计算出整个的最优解。 下面是一个简单的示例,演示如何使用DP解决一个二叉中节点权值之和的最大值问题: ```python class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right def max_sum(root): if root is None: return 0 # 递归计算左右子的最大权值和 left_sum = max_sum(root.left) right_sum = max_sum(root.right) # 当前节点的最大权值和为当前节点值加上左右子中较大的权值和 return root.val + max(left_sum, right_sum) # 构建一个二叉 root = TreeNode(1) root.left = TreeNode(2) root.right = TreeNode(3) root.left.left = TreeNode(4) root.left.right = TreeNode(5) # 计算二叉中节点权值之和的最大值 result = max_sum(root) print(result) ``` 这段代码中,我们定义了一个`TreeNode`类来表示二叉的节点,其中`val`表示节点的权值,`left`和`right`分别表示左子节点和右子节点。`max_sum`函数使用递归的方式计算二叉中节点权值之和的最大值,通过比较左右子的最大权值和来确定当前节点的最大权值和。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值