组合数学与逆元

一、组合数学基础

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 $ 时:

  1. 预处理阶乘数组 $ fact[i] = i! \mod p $。

  2. 预处理逆元数组 $ 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;
    }
    
    
  3. 组合数公式:
    \(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 $ 进制下的各位数字。


四、常见问题

  1. 如何避免计算阶乘时的溢出?
    在每一步乘法后取模

  2. 模数 $ p $不是质数怎么办?
    使用扩展欧几里得算法求逆元,但需保证 $ a $ 和 $ p $ 互质。

  3. 逆元不存在的条件?
    当 $ \gcd(a, p) \neq 1 $ 时,逆元不存在(例如 $ a $ 是偶数,$ p $ 是偶数)。


五、总结

组合数学和逆元是信息学竞赛中解决计数问题和模运算的核心工具。重点掌握:

  • 组合数的递推、性质和预处理方法。
  • 逆元的三种求法及适用场景。
  • 组合数取模的高效实现(阶乘+逆元预处理)。
posted @ 2025-03-12 09:26  hsy2093  阅读(85)  评论(0)    收藏  举报