计数学习笔记
2026-03-07
1 排列组合原理
1.1 基础计数原理
- 加法原理:
完成一件事有 \(n\) 类方法,每类方法数为 \(A_i\),则总方法数为 \(\sum A_i\)
- 乘法原理:
完成一件事有 \(n\) 个步骤,每步方法数为 \(B_i\),则总方法数为 \(\prod B_i\)
1.2 排列与组合
- 排列数:从 \(n\) 个不同元素中取 \(m\) 个按顺序排列,
\[A_n^m = \frac{n!}{(n-m)!}! \]
- 组合数:从 \(n\) 个不同元素中取 \(m\) 个不考虑顺序,
\[C_n^m = \frac{n!}{m!(n-m)!} \]
1.3 卢卡斯定理
- 定理:对于素数 \(p\)
\[C_n^r \bmod p = \prod C_{n_i}^{r_i} \bmod p \]
- 递归形式:
\[C_n^r \equiv C_{n \bmod p}^{r \bmod p} \cdot C_{\lfloor n/p \rfloor}^{\lfloor r/p \rfloor} \pmod{p} \]
1.4. 容斥原理
用于计算多个集合的并集,避免重复计数
公式:
\[|A \cup B \cup C| = |A| + |B| + |C| - |A\cap B| - |B\cap C| - |C\cap A| + |A\cap B\cap C| \]
1.5. 特殊的排列组合问题
1.5.1 圆排列
从 \(n\) 个元素中取 \(m\) 个排成圆,旋转重合视为相同
公式:
\[\frac{A_n^m}{m} \]
- 注意:
当 \(m=n\) 时,圆排列数为 \((n-1)!\)
- 与环排列区别:
圆排列只考虑旋转,环排列还考虑翻转
1.5.2 有重复元素的全排列
若有 \(k\) 种元素,数量分别为 \(n_1,\dots,n_k\)
公式:
\[\frac{(n_1+\cdots+n_k)!}{n_1! \cdots n_k!} \]
1.5.3 不相邻组合
从 \(n\) 个不同元素中选 \(r\) 个,要求任意两个不相邻
公式:
\[C_{n-r+1}^{r} \]
1.5.4 有重复元素的组合
从 \(n\) 种元素中选 \(r\) 个
公式:
\[C_{n+r-1}^{r} \]
1.5.5 错位排列
\(n\) 个元素重新排列,使得每个元素都不在原来的位置上
递推公式:
\[D_n = (n-1)(D_{n-1}+D_{n-2}) \]
- 注意:
其中 \(D_1=0, D_2=1\)
- 通项公式:
\[D_n = n! \sum_{k=0}^n \frac{(-1)^k}{k!} \]
1.5.6 球盒问题
设球 \(n\) 个,盒 \(k\) 个:
球 盒 是否可空 公式 相同 不同 否 \(C_{n-1}^{k-1}\) 相同 不同 是 \(C_{n+k-1}^{k-1}\) 相同 相同 否 \(P(n,k)\) 相同 相同 是 \(P(n+k,k)\) 不同 相同 否 \(S(n,k)\) 不同 相同 是 \(\sum_{i=1}^k S(n,i)\) 不同 不同 否 \(k! \cdot S(n,k)\) 不同 不同 是 \(k^n\)
2 矩阵快速幂
2.1 快速幂
- 普通快速幂:
计算 \(a^b \bmod p\)
时间复杂度 \(O(\log b)\)
原理:将指数二进制分解,利用幂的倍增性质
2.2 矩阵乘法
- 定义:\(C = A \times B\),其中 \(C_{ij} = \sum_k A_{ik} B_{kj}\)
- 乘法条件:\(A\) 的列数等于 \(B\) 的行数
- 性质:
- 不满足交换律,但满足结合律 \((AB)C = A(BC)\)
- 单位矩阵 \(I\) 相当于数的 1
2.3 矩阵快速幂
- 将快速幂推广到矩阵,要求矩阵为方阵
- 模板:定义矩阵结构体,重载乘法,使用快速幂思想计算 \(M^k\)
2.4 应用:线性递推
- 斐波那契数列 \(F_n = F_{n-1}+F_{n-2}\) 可用矩阵表示:
\[\begin{bmatrix} F_n & F_{n-1} \end{bmatrix} = \begin{bmatrix} F_{n-1} & F_{n-2} \end{bmatrix} \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix} \]初始向量 \([F_1, F_0]\) 乘上转移矩阵的 \(n-1\) 次幂即可得 \([F_n, F_{n-1}]\)
2.5 构造转移矩阵的一般方法
- 步骤:
- 确定状态向量 \(V_{old}\),包含递推所需的所有项以及可能需要的辅助变量
- 写出目标向量 \(V_{new}\),它由 \(V_{old}\) 通过递推关系得到
- 矩阵的每一列对应 \(V_{new}\) 的一个分量,列中的系数由该分量的表达式给出
- 常见类型:
- 线性齐次递推:如 \(f_n = a f_{n-1} + b f_{n-2}\),状态向量取 \([f_{n-1}, f_{n-2}]\)
- 带常数项:如 \(f_n = f_{n-1} + f_{n-2} + c\),需引入常数 1,状态向量 \([f_{n-1}, f_{n-2}, 1]\)
- 带多项式:如 \(f_n = f_{n-1} + n^2\),需维护 \(n^2, n, 1\),因为它们之间有递推关系 \((n+1)^2 = n^2 + 2n + 1\)
- 带前缀和:如同时求 \(S_n = \sum_{i=1}^n f_i\),可将 \(S_{n-1}\) 加入状态,利用 \(S_n = S_{n-1} + f_n\) 构造
2026-03-21
3. 序列计数
在构造长度为 $ n $ 的序列时,我们通常有两种递推思路
3.1 位置挨着放,维护值的状态
核心思想:
我们按位置顺序依次填入数字,此时,关注的是已经用了哪些值
状态定义:
通常是 $ dp[i][j] $ 表示已经填了前 $ i $ 个位置,且这 $ i $ 个位置中相对大小关系的某种状态
关键点:
当插入一个新元素时,由于不知道后续数字的具体大小,我们通常只关心它的相对排名
这种模型下的转移往往依赖于“插入一个数后,有多少个排名比它小”
3.2 值挨着放,维护位置的状态
核心思想:
我们按数值大小顺序依次插入数字,此时,关注的是这些数字在最终序列中的位置分布
状态定义:
通常用 $ dp[i][j] $ 表示已经插入了前 $ i $ 小的数字,这些数字在序列中形成了 $ j $ 个连续段
关键点:
插入最大的数时,它要么新建一个段,要么连接两个已有的段
这种思路是连续段DP的基础
3.3 总结
位置放值:
适合限制与位置顺序有关的问题
值放位置:
适合限制与数值相邻有关的问题
4. 相对大小计数
原理:
在处理排列时,如果我们不关心具体的数值大小,只关心相对大小,那么所有排列在概率意义上是等价的
利用这个性质,我们通常可以建立关于前缀最大值或记录的递推示例:
计算长度为 $ n $ 的排列中,前缀最大值恰好有 $ k $ 个的排列数
这其实就是第一类斯特林数的变体
5. 连续段DP
这是处理排列构造问题的一种强大技巧
5.1 核心思想
顺序:
按数值按顺序依次插入
状态:
维护当前已经插入的数字形成的连续段的数量
连续段的定义:
在最终的排列中,如果两个位置相邻且它们的数值是连续的,则属于同一个段
5.2 模板与转移
假设我们插入当前最大的数字 $ i $,当前有 $ j $ 个连续段,考虑插入的方式:
5.2.1 新建段
插入的数字独立成段,不与任何已有段相邻
转移:$ dp[i][j] \leftarrow dp[i-1][j-1] \times (\text{可插入的空隙数}) $
通常可插入的空隙数为 $ j $ 或 $ j+1 $,具体取决于是否允许放在两端
5.2.2 合并两个段
插入的数字正好填补了两个已有段之间的空隙,将它们连接成一个段
转移:$ dp[i][j] \leftarrow dp[i-1][j+1] \times (\text{合并的方式数}) $
通常合并方式数为 $ j $
5.2.3 粘附在段的一端
不合并,仅延长
这是很多问题中还会出现的第三种情况:插入的数字紧挨着某个段的一端,但没有连接两个段
转移:$ dp[i][j] \leftarrow dp[i-1][j] \times (2j) $
5.3 特点
连续段之间是有序的:
在模板中,我们并不关心段与段之间的具体顺序,只在合并时通过组合数考虑如何选择空隙
适用范围:
解决“排列中相邻数差为1的段”、“排列的峰谷数量”、“构造特定结构的排列”等问题

学习计数时记的笔记
浙公网安备 33010602011771号