一些典型的计数
这里将会记录一些典型的计数。
图计数
无向图计数
显然, 个点的无向图个数应该为 。
个点 条边无向图计数
不妨设 表示 个点 条边的无向图个数,显然有
设 表示 个点 条边的无向连通图个数。对于无向图,枚举 号点相连的连通块大小以及边数,可以得到下面等式:
将 的项分离出来,我们可以得到:
这个公式看起来有点繁琐,不妨设 表示 ,移项后不难得到:
复杂度达到 。使用斯特林反演可以做到 。
点无向连通图计数
和上面的思路基本相同。最暴力的思路是枚举 号节点所在连通块的大小。
不妨设 表示 个点的 无向图 个数, 表示 个点的 无向连通图 个数,那么有:
将 分离并移项,可以得到:
这个做法是 的。
接下来你发现,若设出 的 分别为 ,上述式子可以直接分治 NTT。这样可以做到 的复杂度。
树的拓扑序计数
这里的树指的是一棵根向树。
- 使用树形 求解
不妨设 表示以 为根的树的拓扑序计数。不妨设其有两个儿子 。这里的转移不可以将 简单相乘,还应该考虑两个子树之间的错排顺序。考虑从 中选出 个放在前面,其余的放在后面。那么有方程
推广到一般树,就有
时间复杂度 。
- 使用生成函数求解
不妨设 表示 子树选择了 个节点的拓扑序方案数。 为方案数的 。那么有
其中乘上 表示选择自己的方案数。
使用 NTT 即可做到 。所求即为 。
二叉树计数
著名的卡特兰数。不妨设 表示 个点的二叉树个数。那么有递推式:
这是卡特兰数的递推式,答案就是
特殊二叉树计数
CF438E The Child and Binary Tree
给定集合 ,每个节点权值需要 。二叉树的权值定义为其所有节点的权值和。求权值为 的二叉树总数。
发现是上一道题的强化版。普通二叉树计数是这道题 的弱化版。
不妨设 表示总和为 的满足条件的二叉树个数,,则有
其意义为:左子树方案为 ,右子树方案为 ,本身方案为 ,加上空树。
解二次方程得到
上多项式就可以了。如果嫌麻烦可以分子有理化一下。
竞赛图三元环计数
考虑容斥。如果是无向完全图,则三元环个数显然是 。减去不合法的方案即可。
不合法的方案就是从 出发的两条同向边。所以总方案即为
球盒问题
:对某些进行了生成函数的理解。
若干个小球放进若干个盒子的方案计数。按照盒子 / 小球的限制分为
-
小球有标号 / 无标号
-
盒子有标号 / 无标号
-
每个盒子至多放一个 / 至少放一个 / 无限制
共有 种选法。故称 球盒( 重计数法)。
只能掌握比较简单的。比较困难的无法掌握。
小球有标号,盒子有标号,无限制
每个小球可以在 个盒子中选择。所以答案为 。
生成函数理解:由于小球和盒子都不相同,因此都考虑其 。盒子的 为 ,则答案为
小球有标号,盒子有标号,每个盒子至少放一个
考虑容斥。强制有 个盒子里没有球,那么 个球要放进剩下 个盒子里,方案数为 。由于盒子有标号,所以还要乘以一个 。因此总方案数就是:
生成函数理解:每个盒子的 为 ,表示抠掉选 个的方案。答案即为
使用二项式定理展开,可以得到:
小球有标号,盒子有标号,每个盒子至多放一个
如果 肯定无解。假设 ,那么有 个盒子是空的。选出这 个盒子,方案数为 。由于小球有标号,所以还要乘以小球的排列数。答案即为:
生成函数理解:每个盒子生成函数为 ,表示选 个。答案即为:
小球无标号,盒子有标号,每个盒子至少放一个
插板法。将 个小球排成一排,在 个空里面插入 个板子,分成 份。答案即为
生成函数理解:盒子的生成函数为 。这是由于每个盒子放 都有一种方案,其生成函数为 。但是去掉不放小球的方案,即为 。所求即为
小球无标号,盒子有标号,无限制
插板法变形。将 个小球排成一排,在后面再加入 个小球,现在小球总数为 。将 个板子插到 个空里,将小球分成 份。分完之后,再从每一组里抽走一个小球,即可将每个盒子至少放一个转化为每个盒子里放 个。总方案数即为:
生成函数理解:每个盒子的 为 。
注:这里很有可能是不对的。
小球无标号,盒子有标号,每个盒子至多放一个
思路和 小球有标号,盒子有标号,每个盒子至多放一个 类似。如果 无解。然后从 个盒子里选出 个非空盒子即可。这里由于小球无标号,所以不需要乘以 。答案即为
小球有标号,盒子无标号,无限制
就是 第二类斯特林数。
令 表示 个有标号小球放进 个盒子(盒子非空)的方案数,则有递推
对于该式的理解:第 个小球单独放一个盒子,加上和其他 个小球放一个盒子。
可以做到 的递推。多项式做法已经不想懂了。
对于该题,答案即为
小球有标号,盒子无标号,每个盒子至多放一个
能放下就是 ,否则就是 。
小球有标号,盒子无标号,每个盒子至少放一个
和第二类斯特林数定义相同。方案即为
小球无标号,盒子无标号,每个格子至多放一个
能放下就是 ,否则就是 。
小球无标号,盒子无标号,无限制
定义“划分数” ,表示将 划分成 个非负整数的方案数。则有
对于这个式子的理解:显然,对于划分出来的所有数都为整数的情况,将他们全部减一,可以建立到 的双射。对于有 的情况,可以看做在后面加了一个零进去,方案数为 ,加入多个零可以通过这个递归定义。
显然可以多项式,但是我已经不想学了。所以这是一个 的算法。
小球无标号,盒子无标号,每个盒子至少放一个
将每个盒子里先钦定一个球,然后就是上一问的做法。方案数即为
其他计数
错排数
对于一个排列 ,若满足 ,则称 为长度为 的一个错排。
设 表示长度为 的错排的个数。有递推式:
对这个递推式的解释:考虑第 个数放在那里。显然不能放在位置 。假设放在了位置 ,则位置 的数有两种选择:放在 和不放在 。对于放在 ,剩下的方案数就是 ,对于不放在 ,剩下的方案就是 。枚举 有 中选择。故有上式。
部分错排数
对于长度为 的,有 个数错排的排列进行计数。
首先选出 个错排的数,再乘上错排数即可。方案数即为
卡特兰数
上文中二叉树的计数使用了卡特兰数。其符号一般记做 。
卡特兰数有递推式:
通过生成函数等方法,我们可以求出他的另外两个表达式。这两种表达式允许 地计算其值。
卡特兰数最典型的模型是这样的:在 的网格图中,从原点出发到 点,不经过直线 的路径数目。其他问题大多可以转化为这个模型。
卡特兰数典型问题:
-
某些网格路径计数
-
合法括号计数
-
二叉树计数
-
多边形三角形划分
第一类斯特林数
将 个不同元素划分为 个圆排列的方案数,记做 。
考虑递推求第一类斯特林数。有递推式:
对该公式的理解:第 个元素有两种放法:自己单独成为一个圆排列或者与前面的元素共同构成圆排列,这分别对应上式中的两项。
read(n, m); S[0][0] = 1;
rep(i, 1, n) rep(j, 1, m)
S[i][j] = (S[i - 1][j - 1] + (i - 1) * S[i - 1][j] % mod) % mod;
printf("%lld\n", S[n][m]); return 0;
第二类斯特林数
在 重计数法中有提到。其定义为:将 个不同元素划分成 个非空集合的方案数。递推式为:
其理解可以查阅上文球盒问题,小球有标号,盒子无标号部分。
read(n, k); S[0][0] = 1;
rep(i, 1, n) rep(j, 1, k)
S[i][j] = (S[i - 1][j - 1] + j * S[i - 1][j] % mod) % mod;
printf("%lld\n", S[n][k]); return 0;
贝尔数
记 表示将 个不同元素划分成若干个非空子集的方案数。
很显然的,我们已经有了一个 的递推式。根据第二类斯特林数,我们只需要将斯特林数行求和即可。故有
下面介绍另一个递推公式:
对于这个公式的理解:对于第 个数,如果其单独被分到一类,剩下 个,则方案数为 。如果和某一个分到一个集合,则剩下 个,方案数为 。以此类推。
B[0] = 1;
rep(i, 1, n) rep(k, 0, i - 1) (B[i] += B[k] * C(i - 1, k) % mod) %= mod;
不同计数常见值
省选临近,最重要的事情莫过于学会各种乱搞技巧。
对于计数题,最常见的方法是打表 + 瞎几把凑系数。所以能够通过表观察出系数是很重要的能力。
下面给出一些常见系数的表:
- 第一类斯特林数
- 第二类斯特林数
- 卡特兰数
- 错排数
- 划分数
- 贝尔数
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示