题目大意

如果两棵树可以通过重标号后变为完全相同,那么它们就是同构的。

将中间节点定义为度数大于 \(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的更多相关文章

  1. CF724F Uniformly Branched Trees

    CF724F Uniformly Branched Trees 有根树可以统计.无根树难以统计.因为可以换根. 所以不让换根:只要两个无根树在重心位置不同构,就一定不同构 每个本质不同的树在重心位置统 ...

  2. 【CF724F】Uniformly Branched Trees 动态规划

    [CF724F]Uniformly Branched Trees 题意:询问n个点的每个非叶子点度数恰好等于d的不同构的无根树的数目. $n\le 1000,d\le 10$. 题解:先考虑有根树的版 ...

  3. 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 ...

  4. 「日常训练」Paths and Trees(Codeforces Round 301 Div.2 E)

    题意与分析 题意是这样的,定义一个从某点出发的所有最短路方案中,选择边权和最小的最短路方案,称为最短生成树. 现在求一棵最短生成树,输出总边权和与选取边的编号. 我们首先要明白这样一个结论:对一个图求 ...

  5. 「CodeForces 581D」Three Logos

    BUPT 2017 Summer Training (for 16) #3A 题意 给你三个矩形,需要不重叠不留空地组成一个正方形.不存在输出-1,否则输出边长和这个正方形(A,B,C表示三个不同矩形 ...

  6. 「CodeForces - 50C 」Happy Farm 5 (几何)

    BUPT 2017 summer training (16) #2B 题意 有一些二维直角坐标系上的整数坐标的点,找出严格包含这些点的只能八个方向走出来步数最少的路径,输出最少步数. 题解 这题要求严 ...

  7. 「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位置插 ...

  8. 「CodeForces - 717E」Paint it really, really dark gray (dfs)

    BUPT 2017 summer training (for 16) #1H 题意 每个节点是黑色or白色,经过一个节点就会改变它的颜色,一开始在1节点.求一条路径使得所有点变成黑色. 题解 dfs时 ...

  9. 【CF724F】Uniformly Branched Trees

    题意:询问n个点的每个非叶子点度数恰好等于d的不同构的无根树的数目. n≤1000,d≤10n≤1000,d≤10. 题解: 这题真的是一道非常好的题 首先考虑有根树 定义f[i][j][k]表示i个 ...

随机推荐

  1. 对于vue项目更新迭代导致上传至服务器后出现Loading chunk {n} failed和Unexpected token <的解决方式

    相信大家对于vue项目的维护与更新中会遇见很多问题,其中有两种情况最为常见. 一种是Loading chunk {n} failed,这种情况出现的原因是vue页面更新上传至服务器后,由于vue默认打 ...

  2. cephfs文件系统场景

    创建cephfs文件系统: [cephfsd@ceph-admin ceph]$ cd /etc/ceph [cephfsd@ceph-admin ceph]$ ceph fs ls No files ...

  3. 初学js正则表达式之密码强度验证

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  4. 一起手写吧!ES5和ES6的继承机制!

    原型 执行代码var o = new Object(); 此时o对象内部会存储一个指针,这个指针指向了Object.prototype,当执行o.toString()等方法(或访问其他属性)时,o会首 ...

  5. 学习Vue源码前的几项必要储备(一)

    从接下来的一段时间里,Mg要进行阅读源码的工作.再阅读源码前,梳理一下准备工作. 7项重要储备 Flow 基本语法 发布/订阅模式 ES6+ 语法 原型链.闭包 函数柯里化 event loop 1. ...

  6. 【leetcode】122.Best Time to Buy and Sell Stock II(股票问题)

    You are given an integer array prices where prices[i] is the price of a given stock on the ith day. ...

  7. C++字节对齐(对象大小)

    内部数据成员对齐参考这篇 https://www.cnblogs.com/area-h-p/p/10316128.html 这里只强调C++字节对齐特点 ①静态数据成员属于类域,在对象中不占大小 ②若 ...

  8. oracle中的控制语句

    一.条件语句1.流程控制-if else(1)ifif 判断条件 then      ...end if;(2)if-elseif 判断条件 then      ...else      ...end ...

  9. Can we use function on left side of an expression in C and C++?

    In C, it might not be possible to have function names on left side of an expression, but it's possib ...

  10. CSS伪类选择器实现三角形

    使用css实现常用的三角效果 项目中三角: .breadcrumb{ height: 40px; line-height: 40px; padding: 0 20px; border-top: 1px ...