「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个 ...
随机推荐
- 在 Apple Silicon Mac 上 DFU 模式恢复 macOS 固件
DFU 模式全新安装 macOS Big Sur 或 macOS Monterey 请访问原文链接:https://sysin.org/blog/apple-silicon-mac-dfu/,查看最新 ...
- 强化学习实战 | 表格型Q-Learning玩井字棋(二)
在 强化学习实战 | 表格型Q-Learning玩井字棋(一)中,我们构建了以Game() 和 Agent() 类为基础的框架,本篇我们要让agent不断对弈,维护Q表格,提升棋力.那么我们先来盘算一 ...
- C++11的auto自动推导类型
auto是C++11的类型推导关键字,很强大 例程看一下它的用法 #include<vector> #include<algorithm> #include<functi ...
- c学习 - 第五章:选择结构程序设计
5.2 关系运算符与逻辑运算符 !(非) ^ 高 算术运算符 | 关系运算符 | &&和 || | 赋值运算符 | 低
- linux 常用清空文件方法
1.vim 编辑器 vim /tmp/file :1,$d 或 :%d 2.cat 命令 cat /dev/null > /tmp/file
- SpringMVC注解详情
@Component.@Repository @Service.@Controller 看字面含义,很容易却别出其中三个: @Controller 控制层,就是我们的action层 @Service ...
- Linux基础命令---lftp登录ftp服务器
lftp lftp指令可以用来登录远程ftp服务器,这是一个字符界面的文件传输工具. 此命令的适用范围:RedHat.RHEL.Ubuntu.CentOS.SUSE.openSUSE.Fedora. ...
- maven打包插件详解
maven-jar-plugin插件的使用及详解 该插件的xml配置及详解如下: <plugin> <groupId>org.apache.maven.plugins</ ...
- 1.Thmeleaf模板引擎
1.Thmeleaf的基本语法 大部分的Thmeleaf表达式都直接被设置到HTML节点中,作为HTML节点的一个属性存在,这样同一个模板文件既可以使用浏览器直接打开,也可以放到服务器中用来显示数据, ...
- centos添加本地yum源
一.简介 centos6系列于2020年11月份已经停止提供服务,现在各大镜像源已经关闭centos6的yum源,需要下载镜像后在本地搭建yum源方便使用. 最好将镜像下载后传到OSS中,这样从阿里云 ...