组合数学与逆元
一、组合数学基础
1. 排列与组合
- 排列(Permutation):从 $ n $ 个元素中选 $ k $ 个有序排列的方式数:
\(A(n, k) = \frac{n!}{(n-k)!}\) - 组合(Combination):从 $ n $ 个元素中选 $ k $ 个无序组合的方式数:
\(C(n, k) = \binom{n}{k} = \frac{n!}{k!(n-k)!}\)
递推关系(杨辉三角):
\(\binom{n}{k} = \binom{n-1}{k-1} + \binom{n-1}{k}\)
2. 组合数性质
- 对称性:\(\binom{n}{k} = \binom{n}{n-k}\)
- 二项式定理:
\((a + b)^n = \sum_{k=0}^n \binom{n}{k} a^{n-k}b^k\) - 多项式系数(多重组合数):
\(\binom{n}{k_1, k_2, \dots, k_m} = \frac{n!}{k_1!k_2!\dots k_m!} \quad (\text{其中 } k_1 + k_2 + \dots + k_m = n)\)
3. 容斥原理
容斥原理是解决多集合交并问题的核心工具,用于计算多个集合的并集大小。
当直接计算并集困难时,通过各集合及其交集的加减组合求解。
-
公式:
-
两个集合:
\(∣A∪B∣=∣A∣+∣B∣−∣A∩B∣\)
-
三个集合:
\(∣A∪B∪C∣=∣A∣+∣B∣+∣C∣−∣A∩B∣−∣A∩C∣−∣B∩C∣+∣A∩B∩C∣\)
-
通用公式(n个集合)计算多个集合的并集大小:
\(|A_1 \cup A_2 \cup \dots \cup A_n| = \sum |A_i| - \sum |A_i \cap A_j| + \sum |A_i \cap A_j \cap A_k| - \dots + (-1)^{n-1}|A_1 \cap \dots \cap A_n|\)
-
-
应用:解决“至少满足一个条件”或“不满足任何条件”的问题。
计数问题:求满足多个条件之一的元素总数
数论问题:统计范围内能被多个数整除的数字数量
概率计算:多个事件至少发生其一的概率
4. 鸽巢原理(抽屉原理)
- 基本形式:将 \(n\) 个物品放入\(m\)个抽屉,若\(n > m\),则至少有一个抽屉有至少 \(\lceil n/m \rceil\)个物品。
- 竞赛应用:证明存在性问题(如数组中的重复元素)。
二、逆元(Modular Inverse)
1. 为什么需要逆元?
在模运算中,除法不封闭,即 \(a/b \mod p\) 无法直接计算。逆元将除法转换为乘法:
\(\frac{a}{b} \mod p = a \cdot b^{-1} \mod p\)
其中\(b^{-1}\) 是 $ b $ 在模 $ p $ 下的逆元。
2. 逆元的定义
若 $ a $ 和 $ p $ 互质(即 $ \gcd(a, p) = 1 $),则存在唯一整数 $ a^{-1} $ 使得:
\(a \cdot a^{-1} \equiv 1 \mod p\)
3. 求逆元的方法
(1) 费马小定理($ p $ 为质数时)
当 $ p $ 是质数时,根据费马小定理:
\(a^{p-1} \equiv 1 \mod p \implies a^{-1} \equiv a^{p-2} \mod p\)
- 用快速幂计算:
逆元:如果\(a\)能够整除\(b\),希望找到一个数,实现\(\frac{a}{b}\equiv a*x(mod\quad p)\)
这个数\(x\)即叫做\(b\)模\(p\)的逆元,记做\(b^{-1}\),所以\(\frac{a}{b}\equiv a*b^{-1}(mod\quad p)\)
左右两边把\(a\)删掉,同乘一个\(b\),也就是\(b*b^{-1}\equiv1(mod\quad p)\)
由费马小定理可得到,如果\(p\)是质数,且\(b\)和\(p\)互质,那么\(b^{p-1}\equiv1(mod\quad p)\),得到\(b*b^{p-2}\equiv1(mod\quad p)\)
对比逆元的公式,如果b是质数,那么b的逆元即等于\(b^{p-2}\),即可以利用快速幂求解\(b^{p-2}\)
int res = quick_mi(a, p-2, p);
//如果a和p互质,那么a的p-2次方即为逆元
if(a % p != 0) printf("%d\n", res);
//如果p是a的因子,那么对于a而言,不存在逆元
else print("impossible");
(2) 扩展欧几里得算法(通用方法)
求解方程 $ a \cdot x + p \cdot y = 1 $的整数解 $ x $,即为逆元。
int exgcd(int a, int b, int &x, int &y){
//b==0时找到结果
if(!b){
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
//公式推导
y -= a / b * x;
return d;
}
int main(){
int a, b, x, y;
scanf("%d %d %d %d", &a, &b, &x, &y);
exgcd(a, b, x, y);
}
(3) 线性递推求逆元(预处理 1~n 的逆元)
当需要多次计算逆元时,可预处理:
\(inv[i] = (p - \lfloor p/i \rfloor) \cdot inv[p \% i] \mod p\)
初始条件:$ inv[1] = 1 $。
三、组合数取模的竞赛应用
1. 预处理阶乘与逆元
当需要多次计算 $ C(n, k) \mod p $ 时:
-
预处理阶乘数组 $ fact[i] = i! \mod p $。
-
预处理逆元数组 $ inv_fact[i] = (i!)^{-1} \mod p $。(用到费马小定理/扩欧)
void pre_cal(){ fact[0] = 1; for(ll i = 1; i <= n; i++) fact[i] = fact[i-1] * i % MOD; //case 1: 利用费马小定理求解逆元数组 inv_fact[n] = quick_mi(fact[n], MOD - 2, MOD); //case 2: 利用扩展欧几里得定理求解逆元数组 ll x, y; inv_fact[n] = exgcd(fact[n], MOD, x, y); for(ll i = n - 1; i >= 0; i--) inv_fact[i] = inv_fact[i + 1] * (i + 1) % MOD; } -
组合数公式:
\(C(n, k) = fact[n] \cdot inv\_fact[k] \cdot inv\_fact[n-k] \mod p\)//求组合数 ll comb(ll a, ll b){ return fact[a] * inv_fact[b] % MOD * inv_fact[a-b] % MOD; } //求排列数 ll perm(ll a, ll b){ return fact[a] * inv_fact[a-b] % MOD; }
2. Lucas定理(大模数问题)
当 $ p $ 是质数且 $ n $ 和 $ k $ 非常大时:
\(C(n, k) \mod p = \prod_{i=0}^m C(n_i, k_i) \mod p\)
其中 $ n_i $ 和 $ k_i $ 是 $ n $ 和 $ k $ 在 $ p $ 进制下的各位数字。
四、常见问题
-
如何避免计算阶乘时的溢出?
在每一步乘法后取模 -
模数 $ p $不是质数怎么办?
使用扩展欧几里得算法求逆元,但需保证 $ a $ 和 $ p $ 互质。 -
逆元不存在的条件?
当 $ \gcd(a, p) \neq 1 $ 时,逆元不存在(例如 $ a $ 是偶数,$ p $ 是偶数)。
五、总结
组合数学和逆元是信息学竞赛中解决计数问题和模运算的核心工具。重点掌握:
- 组合数的递推、性质和预处理方法。
- 逆元的三种求法及适用场景。
- 组合数取模的高效实现(阶乘+逆元预处理)。

浙公网安备 33010602011771号