「Codeforces 724F」Uniformly Branched Trees
题目大意
如果两棵树可以通过重标号后变为完全相同,那么它们就是同构的。
将中间节点定义为度数大于 \(1\) 的节点。计算由 \(n\) 个节点,其中所有的中间节点度数都为 \(d\) 的互不同构的树的数量。
答案对大质数取模。\(1\leq n\leq 1000,2\leq d \leq 10,10^{8}\leq \text{mod} \leq 10^9\)。
Solution
Part 1
先来思考一个组合问题:在 \(x\) 个方案中不分顺序地选 \(t\) 种,可重复。求方案数。
这里给出一种非插板法的组合意义的解释:问题在于如何处理“可重复”这个条件。考虑在 \(x\) 种方案后面额外添加进去 \(t-1\) 种方案。前面 \(x\) 种方案中的第 \(i\) 种方案,表示选择了第 \(i\) 种方案。后面的 \(t-1\) 种方案中的第 \(i\) 种方案,表示 选出的 第 \(i+1\) 种方案与第 \(i\) 种一样。可以发现,在这 \(x+t-1\) 种方案中任选 \(t\) 个的方案数就是答案。所以方案数为 \(\dbinom{x+t-1}{t}\)。

Part 2
然后再来看这道题。
如何判断两棵无根树是否同构呢?由于无根树是可以重标号(换根)的,所以我们需要对于每棵无根树找出一个特殊的根,将无根树转化为有根树,才能方便比较同构。
对于无根树而言,能够确定的一个点就是 重心。重心有一个特殊的性质:一棵具有 \(n\) 个节点的无根树,若以该树的重心作为整棵树的根,则任意子树大小都小于 \(\frac{n}{2}\)。有两种情况:单重心 与 双重心。
令 \(dp_{i,j,k}\) 表示节点数为 \(i\),有 \(j\) 棵子树,子树大小都不超过 \(k\) 的有根树数量。
有两种情况:
所有子树大小都不超过 \(k\):\(dp_{i,j,k}←dp_{i,j,k-1}\)。
不满足“所有子树大小都不超过 \(k\)”:不满足“所有子树大小都不超过 \(k\)”意味着至少有一棵子树的大小是 \(k\)。考虑枚举子树大小等于 \(k\) 的子树个数。假设有 \(t\) 棵子树大小等于 \(k\)。由于子树之间是可以相同的(即 可重复),所以这 \(t\) 棵子树的方案数就是从 \(dp_{k,d-1,k-1}\) (\(d\) 就是题目中的 \(d\))种方案中不分顺序地选 \(t\) 种并且可重复的方案数。实际上就是我们最开始思考的那个组合问题。所以方案数为 \(\dbinom{dp_{k,d-1,k-1}+t-1}{t}\)。则 \(dp_{i,j,k}←dp_{i-t\times k,j-t,k-1}\times \dbinom{dp_{k,d-1,k-1}+t-1}{t}\)。
若只考虑单重心,那么答案为 \(dp_{n,d,\lfloor \frac{n}{2}\rfloor}\)。
Part 3
然后考虑双重心的情况。出现了双重心,那么肯定是一条边连接的两个点分别挂着两棵大小相等的子树。大概长这样:

显然只有 \(n\) 是 偶数 时才会出现双重心。如图所示,把整棵树拆成两部分,就转化成了两个单重心。对于其中一部分的方案数为 \(dp_{\frac{n}{2},d-1,\lfloor \frac{n}{2}\rfloor -1}\)。所以双重心的情况的个数为,从 \(dp_{\frac{n}{2},d-1,\lfloor \frac{n}{2}\rfloor -1}\) 种方案中选出 \(2\) 种的方案数,则方案数为 \(\dbinom{dp_{\frac{n}{2},d-1,\lfloor \frac{n}{2}\rfloor -1}}{2}\)。
Part 4
综上所述:
- 当 \(n\) 是奇数时,答案为 \(dp_{n,d,\lfloor \frac{n}{2}\rfloor}\)。
- 当 \(n\) 是偶数时,答案为 \(dp_{n,d,\lfloor \frac{n}{2}\rfloor}-\dbinom{dp_{\frac{n}{2},d-1,\lfloor \frac{n}{2}\rfloor -1}}{2}\)。(为什么是单重心的情况个数减双重心的情况个数呢?因为每个双重心的情况都在前面算单重心的情况中算了两遍,一遍是以其中一个重心为根,一遍是以另一个重心为根。所以去掉重复的部分就是答案。)
Code
注意到要求的组合数 \(\binom{n}{m}\),\(n\) 都比较大,\(m\) 都比较小。
\(C_n^m=\frac{n!}{m!(n-m)!}\) ,因为 \(m\) 非常小,所以 \(m!\) 可以很快被计算出来。
再考虑 \(\frac{n!}{(n-m)!}\)。\(\frac{n!}{(n-m)!}=\frac{n\times (n-1)\times ...\times (n-m+1)\times (n-m)\times (n-m-1)\times ...\times 2\times 1}{(n-m)\times (n-m-1)\times ...\times 2\times 1}=n\times (n-1)\times ...\times (n-m+1)\)。只有 \(m\) 项。也可以暴力计算。
代码里是预处理出 \(\frac{1}{i!}\),然后用这样的方法计算。
另外 \(n\leq 2\) 的时候要特判,dp 边界条件也要注意。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e3+5,M=20;
int n,d,mod,dp[N][M][N],f[M],g[M],ans;
int mul(int x,int n,int mod){
int ans=mod!=1;
for(x%=mod;n;n>>=1,x=x*x%mod)
if(n&1) ans=ans*x%mod;
return ans;
}
void init(){
int n=10;
f[0]=g[0]=1;
for(int i=1;i<=n;i++)
f[i]=f[i-1]*i%mod;
g[n]=mul(f[n],mod-2,mod);
for(int i=n-1;i;i--)
g[i]=g[i+1]*(i+1)%mod; //计算 1/(i!)
}
int C(int n,int m){ //计算组合数
if(n<m) return 0;
int ans=1;
for(int i=1;i<=m;i++)
ans=ans*(n-i+1)%mod;
return ans*g[m]%mod;
}
signed main(){
scanf("%lld%lld%lld",&n,&d,&mod),init();
if(n<=2) puts("1"),exit(0); //特判
for(int i=0;i<=n;i++) dp[1][0][i]=1;
for(int i=2;i<=n;i++) //枚举节点数
for(int j=1;j<=min(i-1,d);j++) //枚举子树个数
for(int k=1;k<=n;k++){ //枚举子树大小的限制
dp[i][j][k]=dp[i][j][k-1]; //所有子树大小都不超过 k 的情况
for(int t=1;i-t*k>0&&j-t>=0;t++){ //t 棵子树大小等于 k
if(k!=1) dp[i][j][k]=(dp[i][j][k]+dp[i-t*k][j-t][k-1]*C(dp[k][d-1][k-1]+t-1,t)%mod)%mod;
else dp[i][j][k]=(dp[i][j][k]+dp[i-t*k][j-t][k-1]*C(dp[k][0][k-1]+t-1,t)%mod)%mod;
}
}
if(n&1) ans=dp[n][d][n/2]; //n 为奇数
else ans=(dp[n][d][n/2]-C(dp[n/2][d-1][n/2-1],2)+mod)%mod; //n 为偶数
printf("%lld\n",ans);
return 0;
}
「Codeforces 724F」Uniformly Branched Trees的更多相关文章
- CF724F Uniformly Branched Trees
CF724F Uniformly Branched Trees 有根树可以统计.无根树难以统计.因为可以换根. 所以不让换根:只要两个无根树在重心位置不同构,就一定不同构 每个本质不同的树在重心位置统 ...
- 【CF724F】Uniformly Branched Trees 动态规划
[CF724F]Uniformly Branched Trees 题意:询问n个点的每个非叶子点度数恰好等于d的不同构的无根树的数目. $n\le 1000,d\le 10$. 题解:先考虑有根树的版 ...
- Intel Code Challenge Final Round (Div. 1 + Div. 2, Combined) F - Uniformly Branched Trees 无根树->有根树+dp
F - Uniformly Branched Trees #include<bits/stdc++.h> #define LL long long #define fi first #de ...
- 「日常训练」Paths and Trees(Codeforces Round 301 Div.2 E)
题意与分析 题意是这样的,定义一个从某点出发的所有最短路方案中,选择边权和最小的最短路方案,称为最短生成树. 现在求一棵最短生成树,输出总边权和与选取边的编号. 我们首先要明白这样一个结论:对一个图求 ...
- 「CodeForces 581D」Three Logos
BUPT 2017 Summer Training (for 16) #3A 题意 给你三个矩形,需要不重叠不留空地组成一个正方形.不存在输出-1,否则输出边长和这个正方形(A,B,C表示三个不同矩形 ...
- 「CodeForces - 50C 」Happy Farm 5 (几何)
BUPT 2017 summer training (16) #2B 题意 有一些二维直角坐标系上的整数坐标的点,找出严格包含这些点的只能八个方向走出来步数最少的路径,输出最少步数. 题解 这题要求严 ...
- 「CodeForces - 598B」Queries on a String
BUPT 2017 summer training (for 16) #1I 题意 字符串s(1 ≤ |s| ≤ 10 000),有m(1 ≤ m ≤ 300)次操作,每次给l,r,k,代表将r位置插 ...
- 「CodeForces - 717E」Paint it really, really dark gray (dfs)
BUPT 2017 summer training (for 16) #1H 题意 每个节点是黑色or白色,经过一个节点就会改变它的颜色,一开始在1节点.求一条路径使得所有点变成黑色. 题解 dfs时 ...
- 【CF724F】Uniformly Branched Trees
题意:询问n个点的每个非叶子点度数恰好等于d的不同构的无根树的数目. n≤1000,d≤10n≤1000,d≤10. 题解: 这题真的是一道非常好的题 首先考虑有根树 定义f[i][j][k]表示i个 ...
随机推荐
- 学习java 7.10
学习内容: List 集合:有序集合,用户可以精确控制列表中每个元素的插入位置 List 集合特点:有序:存储和取出的元素顺序一致 可重复:存储的元素可以重复 增强for循环:简化数组和 Collec ...
- YARP+AgileConfig 5分钟实现一个支持配置热更新的代理网关
YARP 是微软开源的一个反向代理项目,英文名叫 Yet Another Reverse Proxy .所谓反向代理最有名的那就是 nginx 了,没错 YARP 也可以用来完成 nginx 的大部分 ...
- Linux命令之用户权限管理
1.创建组.删除组.修改组名.查看组 groupadd 组名 #添加用户组 groupdel 组名 #删除用户组 groupmod -n 新组名 原组名 #修改用户组名称 groups 用户名 #查看 ...
- 转Android Canvas和Paint基本使用
Android Canvas和Paint基本使用 这篇文章主要介绍下画笔Paint和画布Canvas的基本使用 1.Paint 创建对象Paint mPaint = new Paint(); 常 ...
- Java动态脚本Groovy,高级啊!
前言:请各大网友尊重本人原创知识分享,谨记本人博客:南国以南i 简介: Groovy是用于Java虚拟机的一种敏捷的动态语言,它是一种成熟的面向对象编程语言,既可以用于面向对象编程,又可以用作纯粹的脚 ...
- Linux网络编程入门
(一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端 网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户 ...
- 解决用creact-react-app新建React项目不支持 mobx装饰器模式导致报错问题 。
创建react项目 create-react-app mobx-demo cd my-app npm run start 使用react-app-rewired npm install customi ...
- Python 如何管理类的创建行为
问题 如果我们要给类加上一个属性,只需在定义的时候加上属性就可以了: class Animal: can_fly = True 如果这样的类有很多,我们可以定义一个父类,让其它类继承他就可以了: cl ...
- M-SOLUTIONS Programming Contest 2021(AtCoder Beginner Contest 232) 题解
目录 G - Modulo Shortest Path H - King's Tour 因为偷懒就只写G和H的题解了. G - Modulo Shortest Path 首先可以观察到对于一条从点\( ...
- ANTLR 语法设计
下面学习如何编写语法. 如何定义语法规则 一种语言模式就是一种递归的语法结构. 我们需要从一系列有代表性的输入文件中归纳出一门语言的结构.在完成这样的归纳工作后,我们就可以正式使用ANTLR语法来表达 ...