不会组合计数的OIer不是好的OIer
看什么看,说的就是你
组合原理 | 解析组合 + 计数 DP
前言
这篇是 计数DP 的超级无敌完整版,足足有 \(7316\) 个字(有点少了)
由于笔者学的很杂,所以写的也就很杂
从 [组合类] 到 [简单组合问题] 的错排列都是偏基础的概念
由于笔者的生成函数并未深入的学习,所以就简单带过了(毕竟才初二嘛)
这是我写过最长的数论总结
update 忘了什么时候了 [Stirling 数]的部分加入了[拉赫数],因为考试考了,然后背递推式硬控了一个多小时
update 也忘了 [Catalan 数]部分完善了反射容斥的证明(笔者容斥不好,所以没有深入)
组合类
组合类内的每个元素都定义了一个大小,比如树的大小就是它的节点个数,一群蜜蜂的大小就是里面蜜蜂的数量。
记元素 \(\alpha \in \mathcal A\),则其大小为 \(|\alpha|\),需要满足:
- \(|\alpha| \in N\)
- \(\forall n \in N\) ,以其为大小的元素有限。
用 \(\mathcal{A}_k\) 表示 \(\mathcal{A}\) 中大小为 \(k\) 的元素集合,即 \(\mathcal{A}_k=\{a|a \in A,|a|=k\}\)。
——AppleBlue17
Sequence(序列) 构造
考虑一个组合类 \(\mathcal A\),它的序列构造是:
对应的生成函数:
Amplification (膨胀) 构造
考虑一个组合类 \(\mathcal A\),它的膨胀构造是:
对应的生成函数
笛卡尔积 | 乘法原理
对于两个组合类 \(\mathcal A\) 和 \(\mathcal B\) 其笛卡尔积为:
对应生成函数:
其实就是乘法原理
和/不交并 | 加法原理
对于两个不交的组合类 \(\mathcal A\) 和 \(\mathcal B\),其和为:
对应生成函数:
就是加法原理
简单组合问题
圆排列
从 \(n\) 选 \(r\) 的圆排列
环排列
从 \(n\) 选 \(r\) 的环排列
不相邻排列
从 \(S_n = \{i |i \in ([1,n] \cap \Z)\}\),选出 \(k\) 的不相邻的方案数
可重复集的排列数
集合 \(S=\{n_1 \times a_1, n_2 \times a_2,...,n_k \times a_k\}\),\(n = \sum\limits_{i=1}^k\)
错排列
\(D(0) = 0\),\(D(1)=0\)
球与盒的朱重八
| 球 | 盒子 | 可不可空 | 公式 |
|---|---|---|---|
| 相同 | 不同 | False | \(\dbinom{n-1}{k-1}\) |
| 相同 | 不同 | True | \(\dbinom{n+k-1}{k-1}\) |
| 相同 | 相同 | False | \(P(n,k)\) |
| 相同 | 相同 | True | \(P(n+k,k)\) 或 \(\sum\limits_{i=1}^k P(n,i)\) |
| 不同 | 相同 | False | \(S(n,k)\) |
| 不同 | 相同 | True | \(\sum\limits_{i=1}^k S(n,i)\) |
| 不同 | 不同 | False | \(S(n,k)k!\) |
| 不同 | 不同 | True | \(k^n\) |
《十二重 GF 法》
限制条件分别如下:
\(\text{I}\):球之间互不相同,盒子之间互不相同。
\(\text{II}\):球之间互不相同,盒子之间互不相同,每个盒子至多装一个球。
\(\text{III}\):球之间互不相同,盒子之间互不相同,每个盒子至少装一个球。
\(\text{IV}\):球之间互不相同,盒子全部相同。
\(\text{V}\):球之间互不相同,盒子全部相同,每个盒子至多装一个球。
\(\text{VI}\):球之间互不相同,盒子全部相同,每个盒子至少装一个球。
\(\text{VII}\):球全部相同,盒子之间互不相同。
\(\text{VIII}\):球全部相同,盒子之间互不相同,每个盒子至多装一个球。
\(\text{IX}\):球全部相同,盒子之间互不相同,每个盒子至少装一个球。
\(\text{X}\):球全部相同,盒子全部相同。
\(\text{XI}\):球全部相同,盒子全部相同,每个盒子至多装一个球。
\(\text{XII}\):球全部相同,盒子全部相同,每个盒子至少装一个球。
I
II
III
IV
V
最简单的一个
VI
VII
盒子是 \(\sum\limits_{n\ge 0}x^n = \frac{1}{1-x}\)
或者是满足方程 \(x_1+x_2+x_3+...+x_m=n\) 的非负整数解的数量 \(h_n\)
VIII
IX
盒子是 \(\sum\limits_{n\ge1}x^n = \frac{x}{1-x}\)
X
orz, 用的二元生成函数,看了dalao的才会推(太废雾了
易知:
稍微转一下(记 \(G_m(x,y) = \left[y^m\right]G(x,y)\)
没错,转成一元,再有 \(G_0(x)=1\)
答案为:
如果你刷了很多多项式题,会发现这个形式就是 付公主的背包 的结论
枚举倍数就能做了
XI
你知道的,二元:
有:
XII
元神,启动!
先递推:
转一维:\(G_m(x) = [y^m]G(x,y)\),就有:
初始有:\(G_0(x)=1\),所以,直接求通项:
答案就是:
依旧熟悉的配方:
你知道我要说什么:枚举倍数就完了
Stirling 数 | Stirling Number | 拓展
第一类
记法
用 \(\begin{bmatrix}n\\m\end{bmatrix}\) 表示 \(n\) 个不同元素分为 \(m\) 个圆排列的方案数。
有符号的第一类 Stirling 数
递推
有符号的就改成 \(-\)
生成函数
第二类
记法
\(\begin{Bmatrix}n\\m\end{Bmatrix}\) 表示 \(n\) 个不同元素分为 \(m\) 个集合的方案数
递推
通项公式
生成函数
Stirling 反演
用正交关系易证
正交关系
第一正交关系
第二正交关系
杂项
正交关系的矩阵解释
定义无穷下三角矩阵:
则正交关系表示为:
展开易证
即两类 Stirling 数矩阵互为逆矩阵
下降幂与幂的转换
由 Stirling 反演易得
Kronecker delta 函数
Kronecker delta(克罗内克 \(\delta\)) 函数,记作 \(\delta_{nm}\) 或者 \(\delta(n,m)\),定义如下:(Iverson 括号表示)
其实就是单位矩阵 \(\mathbf{I}\) 的元素
拉赫数 | 第三类Stirling 数
记法
\(L(n,m)\) 表示将 \(n\) 个不同元素分成 \(m\) 个排列的方案数
注意 排列内部是有序的
递推关系
通项公式
注意 由通项逆推递推极其简单,但是由递推推通项极其复杂
生成函数
联系
Catalan 数 | Catalan Number | Catalan Sequence
定义
Catalan 数列是序列
其中 \(C_0 = 1\)
神秘性质
递推
壹
考虑由 \(n\) 个 \(+ 1\) 和 \(n\) 个 \(-1\) 构成的 \(2n\) 项序列
其部分和满足 (式壹)
序列的个数等于第 \(n\) 个 Catalan 数
考虑证明
证明方法是反射容斥
因为直接证明太过复杂,所以考虑正难则反
在 \(2n\) 个位置上选 \(n\) 个,方案数就是 \(\dbinom{2n}{n}\)
记满足 式壹 的序列为可接受的 Acceptable,不满足的为不可接受的 Unacceptable
记可接受的数量为 \(\{A_n\}_{n=0}^{\infin}\) ,不可接受的即为 \(\{U_n\}_{n=0}^{\infin}\)
因此
所以我们考虑第一个是的部分和为负数的 \(k\),即
因为是第一个,所以一定有 \(\sum\limits_{i=1}^{k-1}a_i = 0\),则 \(a_k = -1\)
不失一般性,假设 \(k\) 是奇数,对 \(a_i,\forall i \le k\) 取反 \(a_i = -a_i\)
得到新序列 \(\{a_i’\}_{i=0}^{2n}\) ,此时,有 \(n+1\) 个 \(1\), \(n - 1\) 个 \(-1\),这就说明了我们建立了两种关系的双射
所以有 \(U_n\) 等于在 \(n +1\) 个 \(1\) ,\(n-1\) 个 \(-1\)的排列数
即
所以有
稍微化简
考虑一般情况,有 \(n\) 个 \(1\),\(m\) 个 \(-1\),不妨假设 \(n\ge m\)
则终点为 \((m,n)\) 的下三角格路径数有
不妨展开
这就是拓展的 Catalan 数,后文记作 \(\text{ex-}C_{nm}\)
就一个性质,太好了[喜]
生成函数
设 \(G(x) = \sum_{n\ge 0} C_nx^n\)
由第一种递推关系则有
解出来就是上式了
HVD 格路径
允许走对角的格路径
现在我们仅考虑下三角 HVD 格路径的方案数,不妨记 \(S(n,m:rD)\) 表示到 \((m,n)\) 且有 \(r\) 个对角线的下三角 HVD 路径数
我们可以将 \(r\) 个对角线拆成 \(r\) 个上和右
易知这个问题等价于
的非负整数解个数
有
Schröder 数
大 Schröder 数
定义为
例题
居然有例题
直接套 \(S(n,m)\)
其实本题难在如何通过 \(u\) 求 \(n\) 和 \(m\)
这个嘛,自己想
小 Schröder 数
先鸽着
例题
CF1204E Natasha, Sasha and the Prefix Sums
看到 \(n\) 个 \(+1\), \(m\) 个 \(-1\) ,才学完 Catalan 数肯定就想到了用 Catalan 数,或者类似的思路
在定睛一看,是求最大前缀和之和,肯定不含有 Catalan 数,只能够使用类似的思路了
- 将求和转化成求方案数
因为求方案数往往是比求和简单的
这个时候就想到了经典的统计贡献的方法: \(\sum\limits_{i}i\operatorname{number}(i)\)
其中 \(\operatorname{number}(i)\) 表示贡献恰好为 \(i\) 的方案个数
这个时候,再套一个小结论
其中 \(\operatorname{Greater \ than \ or \ equals \ to}(i)\) 表示贡献 大于等于 \(i\) 的方案个数,为了方便,我们简写为 \(\operatorname{Gt}(i)\)
那么,现在的任务就变成了求 \(\operatorname{Gt}(i)\)
规定 \(+1\) 为 \((+1,+1)\),而 \(-1\) 为 \((+1,-1)\),则从 \((0,0)\) 开始,终点为 \((n+m,n-m)\)
则问题变为:求从 \((0,0) \to (n+m,n-m)\) 且必须触碰或超过 \(y=i\) 的格路径数量
则有两种情况
- \(i \le n-m\)

这个时候无论什么时候走肯定会路过 \(y=i\)
这个时候:
- \(i\gt n-m\) 时
这个时候起点和终点就变成了同侧
欸 ☝ ,用反射容斥(拓展Catalan的思想
将起点沿 \(y = i\) 向上翻折,变为了 \((0,2i)\) ,所以强制有 \(m+i\) 步向下:
所以说答案就变成了:
生成函数
OGF | 常规生成函数
对于无限数列 \(\{a_n\}\) ,其生成函数为:
记为:
常见数列生成函数
生成函数的平移与伸缩
平移
向右:乘上 \(x^k\):
向左:乘上 \(x^{-k}\):
如果加入 \(\vartheta\) 算子(\(\vartheta f = xf'\))
就有神奇的事发生:
\[\vartheta G(x) = xG'(x) = a_1x+2a_2x^2+3a_3x^3+\cdots = \sum_{i\ge0}ia_ix^i \]
伸缩
单位根反演
单位根:满足 \(\omega^k=1\) 的复数 \(\omega\)
有:
(记 \(\omega = e^{\frac{2\pi i}{k}}\)
EGF | 指数生成函数
标准形式
常见数列生成函数
DGF | Dirichlet 生成函数
对于数列 \(\{a_n\}\),其 Dirichlet 生成函数 定义如下:
\[f(s) = \sum_{n\ge1} \frac{a_n}{n^s} \]
Example
数列 \(\{1\}_{n=1}^{\infin}\) 的 Dirichlet 生成函数是 Riemann-Zeta 函数
性质
壹
考虑 \(f(s)\) 、\(g(s)\) 是数列 \(\{a_n\}\)、\(\{b_n\}\) 的 Dirichlet 生成函数
则函数 \(f(s)g(s)\) 是数列
的 Dirichlet 生成函数
贰
若 \(f(s)\) 是数列 \(\{a_n\}\) 的 Dirichlet 生成函数,则函数 \(f^{k}(s)\) 是数列
What happens next
稍微对 \(\zeta(s)\) 套一下性质二,就会发现 \(\zeta^k(s)\) 表示的是将 \(n\) 分成 \(k\) 个有序正因子数的方案的 Dirichlet 生成函数
而 \((\zeta(s) - 1)^k\) 是将 \(n\) 分成 \(k\) 个有序非平凡正因子的方案的 Dirichlet 生成函数
叁
对于一个积性数论函数 \(f(n)\),则 \(\left\{f(n)\right\}_{n=1}^{\infin}\) 的 Dirichlet 生成函数可以表示为
肆
对于 Möbius 函数 \(\mu(n)\) ,易知 \(\{\mu(n)\}_{n=1}^\infin\) 的 Dirichlet 生成函数为
Möbius 反演
对于两个数列 \(\{a_n\}_{n=1}^{\infin}\) 和 \(\{b_n\}_{n=1}^{\infin}\) 满足对任意 \(n\ge1\) ,有
则对于任意的 \(n\ge1\),也会有
例题
AT_abc266_g [ABC266G] Yet Another RGB Sequence
用 \(\text{ans}_i\) 表示 RG 数量恰好为 \(i\) 个时的答案,容易知道:\(k\le i \le \min\{R,G\}\)
所以就有:
边界是:
容易发现,这个递推明显是复杂了
直接推正解容易得到
AT_arc004_4 [ARC004D] 表現の自由 ( Freedom of expression )
有 \(N = \prod\limits_{i=1}^{k}p_i^{\alpha_i}\),所以就有:
如果想要无脑点怎么办
对 \(\zeta(s)\) 套上性质贰,就会发现:
稍微对 \(\zeta(s)\) 套一下性质二,就会发现 \(\zeta^k(s)\) 表示的是将 \(n\) 分成 \(k\) 个有序正因子数的方案的 Dirichlet 生成函数
然后稍微推一下 \([n^{-s}](\zeta^k(s))\) ,就会发现
[ABC209F] Deforestation
本质上是找到一种顺序的构造使得代价最小,而与之相似的构造也就是最小代价了
所以主要任务就是找到顺序的特殊性
找顺序的特殊性不就是相对大小
对于一个排列中的数 \(p_i\) ,由于排列的唯一性,是不会出现 \(p_i\) 在之前被砍倒,所以对于 \(p_i\),就需要尽可能的使 \(H_{p_i-1}\) 与 \(H_{p_i+1}\) 为 \(0\) 了,而对于高度序列 \(\{H_i\}_{1}^{n}\),就需要找到 \(p_i\) 使得 \(H_{p_i}\) 最小,然后使其两侧为 \(0\)
但是,如果这样的话,要考虑的情况就过多了一点
再更进一步,我们会发现,两个不相邻的位置对于贡献无影响,所以只用考虑相邻的情况
对于 \(i\) 以及 \(i+1\) ,考虑先砍 \(i\) 还是 \(i+1\) 的贡献
相减得 \(\Delta H_i\)
知周所众,差分数列是可以反应原数列的相邻两项的大小关系的
将差分数列写成类似 <=> 的形式,就容易发现,这就是我们需要的排列顺序
* 具体的构造:
\(\Delta H < 0\) :放上 \(<\),反之放 \(>\) ,等于 \(0\) 时放上等号
for (int i = 1; i < n; i++) {
if (deltaHeigh[i] == 0) str = str + "=";
else if (deltaHeigh[i] < 0) str = str + "<";
else if (deltaHeigh[i] > 0) str = str + ">";
}
dp[1][1] = 1;
prefixSum[1][1] = 1;
for (int i = 2; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (str[i - 1] == '<') dp[i][j] = prefixSum[i - 1][j - 1];
else if (str[i - 1] == '>') dp[i][j] = prefixSum[i - 1][i - 1] - prefixSum[i - 1][j - 1];
else if (str[i - 1] == '=') dp[i][j] = prefixSum[i - 1][i - 1];
prefixSum[i][j] = dp[i][j];
}
for (int j = 1; j <= i; j++) prefixSum[i][j] = ((prefixSum[i][j] + prefixSum[i][j - 1]) mod MOD + MOD) mod MOD;
}
CF2163C Monopati
枚举 \(l\) 和 \(r\) 的话状态太多,足足有 \(\mathcal O(n^2)\) 种情况,所以肯定不行
但是我们会发现,每一条路径唯一对应一组 \(l\) 和 \(r\),而路径只有 \(\mathcal O(n)\) 条
这说明什么,说明了可以枚举路径
前缀最大值
前缀最大值
定义对于 $\{a_i\}$ ,若 $a_k$ 满足 $\forall i \lt k, a_i \lt a_k$,就称 $a_k$ 为前缀最大值
容易知道,方案数就是无符号的第一类 Stirling 数
总结
相对大小DP 本质在于 “相对大小” 这四个字,当找到了一个可行的顺序(相对大小时,基本上都可以化归成可行的解
在排列计数问题中,我们通常需要统计满足某些大小关系约束的排列个数。直接枚举数值不可行(\(n!\) 太大),但利用相对大小可以设计出多项式时间的DP。
——Deepseek
常见设计:
\(\text{dp}_{ij}\) 表示已经插入了 \(\{1,2,\ldots,i\}\),当前的数相对位置(第几大/小为 \(j\) 的方案数
连续段DP
连续段DP 旨在于状态由相邻的状态牵连时可以快速的、无后效性的推出状态转移方程
基本的操作就是仅仅考虑连续的一段,段与段之间无联系
对于一个连续段,有 增长 Extend 这个操作
对于两个连续段,有 合并 Merge 这个操作
此外,还可以新开一段
正确性
显然,吗?
首先明确,正确性的证明 \(\iff\) 证明连续段DP与 \(N = \{1,2,\ldots,n\}\) 的排列的集合存在双射关系
然后考虑证明从决策序列到排列的映射 \(\Phi : \mathbf D \to N\),\(\mathbf D\) 是决策(Decision) 空间,是由连续段 DP 决定的
而反向映射 \(\Psi : N \to \mathbf D\) 应与 \(\Phi\) 互为逆函数,即对于排列 \(\pi\),有 \(\Phi(\Psi(\pi)) = \pi\)
证明的过程就是找到两个构造方案,太长所以不写了
常见定义方式以及转移
定义
\(\text{dp}_{i,j,\ldots}\) 表示处理完了前 \(i\) 个,有 \(j\) 个连续段的方案数
注意:连续段之间足够远
通常是有 \(\text{dp}_{11,\text{init}} = 1\) 的
这只是最基础的定义,根据题目还需要添加状态(Magneti)
在化归到连续段后,转移都一样了
转移
新开 New
增长 Extend
合并 Merge
例题
Phoenix and Computers
就是一道模版题目
没什么难的
标准的转移,仅仅需要在考虑多一个相邻两个、三个的情况就行了
for (int i = 2; i <= n; i++)
for (int j = 1; j <= n; j++) {
dp[i][j] += dp[i - 1][j - 1] * j;
dp[i][j] += dp[i - 1][j] * j * 2;
dp[i][j] += dp[i - 2][j] * j * 2;
if (i > 2) dp[i][j] += dp[i - 2][j + 1] * j * 2;
if (i > 3) dp[i][j] += dp[i - 3][j + 1] * j;
dp[i][j] = (dp[i][j] % MOD + MOD) % MOD;
}
P7967 [COCI 2021/2022 #2] Magneti
咕,稍微化归一下再顺便转成模版
怎么化归呢?
我们不妨把磁铁的影响范围也看成磁铁,而两块磁铁,我们希望它们尽可能紧地挨在一起
所以对于 \(i\) 与 \(i+1\), 它们间的距离便是 \(\max(r_i,r_{i+1})\)
而为了方便我们操作,将 r[] 排序,这样,当前处理到的 \(i\) 便是最大的
这般,我们就化归成了模板的情况
转移也就十分的简单了,\(\text{dp}_{ijk}\) 表示放了 \(i\) 个,有 \(j\) 个连续段,用了 \(k\) 个空位的方案数
最后统计答案时,剩余的空位要板插进连续段中间
简单滴很呐
dp[0][0][0] = 1;
for (int i = 0; i < n; i++)
for (int j = 0; j <= i; j++) {
for (int k = 0; k < l; k++) {
dp[i + 1][j + 1][k + 1] += (dp[i][j][k] * (j + 1) mod MOD + MOD) mod MOD;
if (j >= 1)dp[i + 1][j][k + r[i + 1]] += (dp[i][j][k] * 2 * j mod MOD + MOD) mod MOD;
if (j >= 2) dp[i + 1][j - 1][k + 2 * r[i + 1] - 1] += (dp[i][j][k] * (j - 1) mod MOD + MOD) mod MOD;
dp[i + 1][j + 1][k + 1] %= MOD;
if (j >= 1) dp[i + 1][j][k + r[i + 1]] %= MOD;
if (j >= 2) dp[i + 1][j - 1][k + 2 * r[i + 1] - 1] %= MOD;
}
}
ll answer = 0;
for (int i = 0; i <= l; i++) answer += ((answer + 1ll * dp[n][1][i] * C(l - i + n, n) mod MOD + MOD) mod MOD + MOD) mod MOD;
P5999 [CEOI 2016] kangaroo
一眼看上去和连续段毫无干系
再仔细一想,便越发地觉得它像相对大小了
注意,如果有上面的想法,便说明我们陷入了思维误区
因为满足 \(a_0<a_1>a_2<a_3>\ldots\) 的方案数是已知的,而本题求得有又类似的方案数,所以我们就会想到如何划归到上面
但是,随着思维的深入,我们发现,相对大小可做是可做,但是光状态定义就有足足三维:\(\text{dp}_{ijk}\) 表示放了前 \(i\) 个,当前放的是相对大小为 \(j\) 的数,上一次的结尾是相对大小为 \(k\) 的方案数
所以肯定不行
现在我们就考虑另一种插入DP——连续段
定义 \(\text{dp}_{ij}\) 表示已经放了前 \(i\) 个,有 \(j\) 个连续段的方案数
答案在 \(\text{dp}_{n1}\)
转移:
New 新建一个肯定是可以的,有
Extend 这是不行的
如果连续段的末尾已经是单调递增的: \(x \lt y\),此时放入的 \(z\) 肯定大于 \(y\) ,这样就会出现 \(x\lt y\lt z\) 的情形,而这是万万不可的
同理,在前面加入也会有类似的情景
Merge 常规操作
但是呢,这么写肯定是会挂掉的[悲]
所以考虑一下细节
当到达 \(s\) 的时候,肯定不可执行 merge 操作,所以 \(\text{dp}_{s,j+1} = \text{dp}_{s-1,j}\cdot (j+1)\)
而在 \(s\) 之后,即 \(i \gt s\) 时,新增段就不能放在 \(s\) 前面了,所以 \(\text{dp}_{i+1,j+1} \longleftarrow \text{dp}_{ij} \cdot j\),\(\text{dp}_{i+1,j-1} \longleftarrow \text{dp}_{ij} \cdot (j-2)\)
CF1606E Arena
这和 Catalan 数有甚么干系
看到这道题,有一种强烈的感觉,好吧,是推出来的
状态定义肯定跟人数有关
那另一维呢,我不知道
模拟一下,假设 \(\{a_n\} = \{1,3,2,5,4\}\)
经过一轮后:\(\{a_n'\} = \{0,0,0,1,0\}\),只剩下了一个最大值
很好,那就记录最大值了
\(\text{dp}_{ij}\),有 \(i\) 个人,最大生命值是 \(j\)
当 \(j \lt i\),\(\text{dp}_{ij} = j^{i}-(j-1)^{i}\)
当 \(j \ge i\) 时,\(\text{dp}_{ij} = \sum\limits_{k=1}^{i}\text{dp}_{k,j-i+1}\cdot (i-1)^{i-k}\cdot \dbinom{i}{k}\)
好,这就没了
解释一下,还是不用了
参考资料
本文章 [《十二重 GF 法》] 的部分就是受上面这篇博客启发
生成函数
Cnblogs Appleblue17 Re:从零开始的生成函数
DP 相关
都看到这儿了,不会还有人不知道摘要是AI写的吧

浙公网安备 33010602011771号