CF724F Uniformly Branched Trees 树形dp 组合数

题目链接

题意:
给你一个 n n n一个 d d d和一个模数,让你求 n n n个点的不同构的无标号树,要求所有除了叶子之外的点的度数都是 d d d的方案数。不同构是指对于任何重标号后的树不同构。 n &lt; = 1000 , d &lt; = 10 n&lt;=1000,d&lt;=10 n<=1000,d<=10

题解:
之前没怎么做过这种无标号无根树不同构的题,于是对怎么处理同构上就不怎么会。

这个题的一个重要问题是很多种dp方式都很难处理不同构,于是怎么保证不算重不算漏就很关键了。对于这种无根树,我们在树形dp一般都是转为有根树来做,那么转为有根树的话就要求我们要选出一个根,都是随便选根的话你很难计算当前的每一种方案与多少种方案在重标号之后同构了。

这时候就迎来了本题的关键点,我们选取树上的一个特殊点当做根,我们选重心做根,因为一棵树的重心通常只有一个,只有点数为偶数的时候可能会出现有两个重心的情况。我们考虑选出重心当这个有根树的根有什么用处,其实我们如果再能保证每种方案的所有子树都不同构,就可以不重不漏的计算出答案了。如果有两个重心的话减去重复计算的答案就可以了。

那么转化成有根树之后就比较好进行dp了。我们设 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]表示有 i i i个点,当前根共有 j j j个子树,每个子树大小不超过 k k k的方案数。我们有 d p [ i ] [ j ] [ k ] = d p [ i ] [ j ] [ k − 1 ] + ∑ l = 1 l &lt; = d , k ∗ l &lt; i d p [ i − k ∗ l ] [ j − l ] [ k − 1 ] ∗ C d p [ k ] [ d − 1 ] [ k − 1 ] + l − 1 l dp[i][j][k]=dp[i][j][k-1]+\sum_{l=1}^{l&lt;=d,k*l&lt;i}dp[i-k*l][j-l][k-1]*C_{dp[k][d-1][k-1]+l-1}^l dp[i][j][k]=dp[i][j][k1]+l=1l<=d,kl<idp[ikl][jl][k1]Cdp[k][d1][k1]+l1l. 解释一下这个式子,就是你子树大小不超过 k k k的可以从都不超过 k − 1 k-1 k1的转移过来,然后我们可以之前子树都是不超过 k − 1 k-1 k1,现在开始是不超过 k k k的了,也就是在当前选了若干个大小是 k k k的子树,而这几个是一个可重组合,于是乘那个组合数。

最后答案就是 d p [ n ] [ d ] [ n / 2 ] dp[n][d][n/2] dp[n][d][n/2],因为重心的性质,每个子树大小都不超过 n / 2 n/2 n/2,然后最后还要再减掉 n n n为偶数并且双重心的情况,也就是减掉 C d p [ n / 2 ] [ d ] [ n / 2 ] 2 C_{dp[n/2][d][n/2]}^2 Cdp[n/2][d][n/2]2

复杂度的话,我由于组合数那里写的不是很优秀,单次算组合数是 O ( d ) O(d) O(d)的,于是我写的总复杂度是 O ( n 2 d 3 ) O(n^2d^3) O(n2d3)的,但是由于跑不满,再加上CF测评机跑得比较快,能过这个题。

代码:

#include <bits/stdc++.h>
using namespace std;

int n,d;
long long mod,dp[1010][11][1010],ni[1000],jie[1000],ans;
inline long long ksm(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;
}
inline long long C(long long x,long long y)
{
	if(x<y)
	return 0;
	long long res=1;
	for(long long i=1;i<=y;++i)
	res=res*(x-i+1)%mod;
	res=res*ni[y]%mod;
	return res;
}
int main()
{
	scanf("%d%d%I64d",&n,&d,&mod);
	if(n<=2)
	{
		printf("1\n");
		return 0;
	}
	jie[0]=1;
	for(int i=1;i<=d;++i)
	jie[i]=jie[i-1]*i;
	for(int i=1;i<=d;++i)
	ni[i]=ksm(jie[i],mod-2);
	for(int i=0;i<=n;++i)
	dp[1][0][i]=1;
	for(int i=2;i<=n;++i)
	{
		for(int j=1;j<i&&j<=d;++j)
		{
			for(int k=1;k<=n;++k)
			{
				dp[i][j][k]=dp[i][j][k-1];
				for(int l=1;l*k<i&&l<=j;++l)
				{
					if(k>1)
					dp[i][j][k]=(dp[i][j][k]+dp[i-k*l][j-l][k-1]*C(dp[k][d-1][k-1]+l-1,l)%mod)%mod;
					else
					dp[i][j][k]=(dp[i][j][k]+dp[i-k*l][j-l][k-1]*C(dp[k][0][k-1]+l-1,l)%mod)%mod;
				}
			}
		}
	}
	ans=dp[n][d][n/2];
	if(n%2==0)
	ans=(ans-C(dp[n/2][d-1][n/2],2)+mod)%mod;
	printf("%I64d\n",ans);
	return 0;
}
内容概要:本文系统梳理了多个科研领域的前沿研究与技术实现,重点涵盖FDTD方法中的完美匹配层(PML)研究,以及Matlab/Simulink在电磁、电力、控制、通信、信号处理、图像处理、路径规划、能源系统优化等领域的仿真与算法实现。文中列举了大量基于Matlab和Python的科研案例,如风电功率预测、负荷预测、无人机三维路径规划、电池系统故障诊断、雷达模拟、通信编码、微电网优化调度等,并强调结合智能优化算法(如粒子群、遗传算法、深度学习等)提升系统性能。同时,提供了丰富的代码资源与仿真模型,涵盖永磁同步电机控制、逆变器设计、多智能体任务分配、虚拟电厂调度等复杂系统,助力科研人员快速开展复现实验与创新研究。; 适合人群:具备一定编程基础,熟悉Matlab/Python工具,从事电气工程、自动化、通信、人工智能、新能源、控制科学等相关领域研究的研发人员及研究生。; 使用场景及目标:① 学习并实现FDTD仿真中的PML边界条件以有效抑制数值反射;② 掌握Matlab/Simulink在多物理场建模、控制系统设计与优化算法中的综合应用;③ 借助提供的代码资源完成科研复现、课程设计、竞赛项目或工程原型开发; 阅读建议:此资源以科研实战为导向,不仅提供理论方法,更强调代码实现与仿真验证。建议读者结合自身研究方向,按目录顺序查阅相关模块,下载配套代码进行调试与二次开发,以达到学以致用、融会贯通的目的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值