组合数学基础
排列组合
组合数
另一个更加通用的形式为
其中 \(n^{\underline{m}}\) 代表下降幂,值为 \(\prod_{i = 0}^{m - 1}(n - i)\)。
这个通用形式可以把组合数的上指标扩展到任意实数,但在算法竞赛中,我们通常只关心 \(n\) 和 \(m\) 都是非负整数的情况,因此本文中出现的数默认非负。
组合数 \(\dbinom{n}{m}\) 可以视作一个关于 \(n\) 的 \(m\) 次多项式。
代码实现
注:都是在取模意义下讨论的。不讨论暴力做法。
-
利用杨辉/帕斯卡恒等式递推。(我们将会在组合恒等式一章中看到它)
Code
for(int i = 0; i <= n; i++) binom[i][0] = 1; for(int i = 1; i <= n; i++) { for(int j = 1; j <= i; j++) { binom[i][j] = (binom[i - 1][j - 1] + binom[i - 1][j]) % MOD; } }
预处理时间复杂度 \(O(nm)\)。单次查询时间复杂度 \(O(1)\)。
该方法的优势在于不需要逆元,适合在模数不是质数的情况下使用。(如 [清华集训2012] 序列操作)
-
利用 \(\dbinom{n}{m} = \dfrac{n!}{(n-m)!m!}\) 计算。
首先可以线性预处理阶乘,于是分子搞定。
至于分母,我们需要计算阶乘的逆元。这也是可以线性计算的。首先要求出 \(\operatorname{inv}(n!)\),然后倒序枚举 $ (n-1) \sim 1$ 的自然数,通过把 \(\operatorname{inv}((i+1)!)\) 乘上 \(i+1\) 以约掉分母中的 \(i+1\),得到 \(\operatorname{inv}(i!)\)。
Code
void init() { fact[0] = 1; for(int i = 1; i <= n; i++) fact[i] = fact[i-1] * i % MOD; fact_inv[n] = getinv(fact[n]); // getinv 可以通过快速幂/扩欧实现 for(int i = n - 1; i >= 0; i--) fact_inv[i] = fact_inv[i+1] * (i+1) % MOD; } int binom(int n, int m) { return fact[n] * fact_inv[n - m] % MOD * fact_inv[m] % MOD; }
时间复杂度 \(O(n) \sim O(1)\)(这里忽略了预处理中求 \(n!\) 逆元的时间复杂度)。
多重集的排列数/多重组合数
总共有 \(n\) 个物品,这些物品分为 \(k\) 种,第 \(i\) 种物品有 \(n_i\) 个,相同种类的物品是不可区分的,则这 \(n\) 个物品的排列数为
解释:如果所有物品都不同,排列数有 \(n!\) 个。但考虑到相同种类的物品不可区分,每种排列实际上被算了 \(\prod_{i=1}^{k} n_i!\) 次,需要除掉。
推论:考虑另一个问题:有 \(n\) 个不同的元素,要分成 \(k\) 组,第 \(i\) 组有 \(n_i\) 个元素,求有多少种分法。
可以建立一个多重集的排列到分法的一一对应。因此这两个问题的答案是相同的,分法的数量也是 \(\dbinom{n}{n_1, n_2, \cdots, n_k}\)。
方程的整数解个数(插板法)
-
已知方程 \(x_1 + x_2 + \cdots + x_k = n\),求满足方程的正整数解的个数。
解:个数为
\[\binom{n-1}{k-1} \] -
已知方程 \(x_1 + x_2 + \cdots + x_k = n\),求满足方程的非负整数解的个数。
解:构造方程 \(y_1 + y_2 + \cdots + y_k = n + k\),其中 \(y_i = x_i + 1\)。该方程的正整数解个数与原方程的非负整数解个数相同(可以构造出一个一一对应关系),而该方程的正整数解个数可以通过上文的方法计算。所以答案为
\[\binom{n + k - 1}{k - 1} \] -
已知方程 \(x_1 + x_2 + \cdots + x_k = n\),且 \(\forall 1\le i \le n, x_i \ge a_ i\),求满足方程的非负整数解的个数。
-
已知方程 \(x_1 + x_2 + \cdots + x_k = n\),且 \(\forall 1\le i \le n, x_i \le a_ i\),求满足方程的非负整数解的个数。
组合恒等式
O. 阶乘展开式
即定义式。为了完整,放在第零个位置。
I. 对称恒等式
使用组合意义证明:从 \(n\) 个物品中选择 \(m\) 个物品的方案数和从 \(n\) 个物品中不选 \(m\) 个物品的方案数一一对应,而后者正是 \(\dbinom{n}{n - m}\)。
II. 吸收恒等式
把式子展开成定义式就可以立即证明这个恒等式。它告诉我们可以将一些系数移进或移出组合数。当把系数移进去时,就像组合数把它“吸收”了一样,因此叫吸收恒等式。
两边同乘 \(m\) 可以得到一个当 \(m = 0\) 时也成立的形式:
此外,这个恒等式还有一个保持两边下指标相同的形式:
可以在两次应用组合恒等式之间代入原式推导出这一结果:
III. 加法公式(杨辉/帕斯卡恒等式)
使用组合意义证明:从 \(n\) 个物品中选择 \(m\) 个物品,有两类方案:要么选择最后一个物品,要么不选择最后一个物品。如果选择最后一个物品,那么还要剩下的 \((n - 1)\) 个物品中选 \((m - 1)\) 个,方案数为 \(\dbinom{n - 1}{m - 1}\);如果不选择最后一个物品,就要在剩下的 \((n - 1)\) 个物品中选择 \(m\) 个,方案数为 \(\dbinom{n - 1}{m}\)。因此,两者的和就是从 \(n\) 个物品中选 \(m\) 个物品的方案数 \(\dbinom{n}{m}\)。
IV. 上指标求和
这个恒等式把一个组合数表示为下指标为常数的组合数之和。从杨辉三角上看,这相当于对某一列求前缀和。
使用组合意义证明:假设有一个集合 \(\{0, 1, \cdots, n\}\),我们想从中选出 \((m + 1)\) 个数,如果选出的数中最大值为 \(k\),就还要从 \(\{0, 1, \cdots, k - 1\}\) 这 \(k\) 个数中选择 \(m\) 个数,方案数为 \(\dbinom{k}{m}\)。
V. 二项式定理
二项式定理表明,把一个二项式 \(x + y\) 的幂展开,\(x\) 和 \(y\) 的系数都是组合数。(这就是组合数又称为二项式系数的原因。)
该定理同样可以用组合意义证明:把乘积
展开时,每一项都是 \(n\) 个 \(x\) 或 \(y\) 的积,即形如 \(x^{i}y^{j}\)。又由于每个括号提供一个因子,所以 \(i + j = k\),并且从 \(n\) 个括号中选择 \(k\) 个 \(x\) 的方案数为 \(\dbinom{n}{k}\),因此 \(x^{k}y^{n - k}\) 的系数为 \(\dbinom{n}{k}\)。
(需要说明的是,如果 \(n\) 不是非负整数,而是负数或者其它任意的实数,二项式定理也仍然成立。这时和式是无限的,因此必须保证 \(|x / y| < 1\) 以使得和式收敛。不过这不在本文讨论范围之内。)
二项式定理有两个重要的推论:当 \(x = y = 1\) 时有
这意味着第 \(n\) 行的组合数之和为 \(2^{n}\)。(遗憾的是,虽然我们有上指标求和公式,但下指标的和没有封闭形式——我们只能求出一整行的和,而不能求出一行的某个前缀的和。)
如果 \(x = -1\) 且 \(y = 1\),那么
也就是说同一行组合数的交错和为 \(0\)。换一个表示形式:
也就是奇数项之和等于偶数项之和。
VI. 三项式恒等式
这个恒等式用来解决两个组合数相乘的问题。虽然它的右边仍然是两个组合数,但 \(m\) 只出现了一次。所以,当关于 \(m\) 求和时,右边的形式往往更方便。
简单地把式子拆开即可证明。
VII. 范德蒙德卷积
使用组合意义证明:从 \(n\) 个男人和 \(m\) 个女人中选择 \(k\) 个人,有 \(\dbinom{n + m}{k}\) 种方法。而如果选择 \(i\) 个男人和 \((k - i)\) 个女人,就有 \(\dbinom{n}{i} \dbinom{m}{k - i}\) 种方法,对 \(i\) 求和就得到了所有的方案。
VIII. 对角线求和
用加法恒等式逐步展开即可证明。