以下C++代码实现了一个用于高效计算组合数(二项式系数)的预处理器,它通过预计算阶乘和阶乘的逆元来加速组合数计算。以下是详细解析:
核心功能
- 预计算阶乘数组
fact[]fact[i] = i! % MOD(i的阶乘对MOD取模)- 例如:
fact[3] = 3! = 6
- 预计算阶乘逆元数组
inv_fact[]inv_fact[i] = (i!)^(-1) % MOD(i的阶乘的模逆元)- 用于避免除法,将除法转化为乘法(数论基础)
- 支持快速计算组合数 组合数公式: 可优化为:
代码逐行解析
const int MOD = 1e9 + 7; // 模数(质数)
const int MX = 1e5; // 预计算的最大范围(100,000)
long long fact[MX]; // 存储阶乘 % MOD
long long inv_fact[MX]; // 存储阶乘的逆元 % MOD
void init() {
// 检查是否已初始化(避免重复计算)
if (fact[0]) return;
// 1. 计算阶乘数组 [0!, 1!, ..., (MX-1)!] % MOD
fact[0] = 1; // 0! = 1
for (int i = 1; i < MX; i++) {
fact[i] = fact[i - 1] * i % MOD; // 递推计算阶乘
}
// 2. 计算阶乘逆元数组
// 2.1 用费马小定理计算最后一个元素的逆元
inv_fact[MX - 1] = qpow(fact[MX - 1], MOD - 2); // 快速幂求逆元
// 2.2 倒序递推计算所有逆元
for (int i = MX - 1; i > 0; i--) {
inv_fact[i - 1] = inv_fact[i] * i % MOD; // 关键递推关系
}
}
关键技巧详解
1. 逆元递推关系(核心优化)
- 递推公式:
- 数学推导: 由定义知: 两边乘以得到 的逆元: (因为 )
2. 倒序计算逆元的原因
- 先计算最大的逆元
inv_fact[MX-1](通过快速幂) - 利用递推关系 反向计算 更小的逆元,避免对每个元素单独调用快速幂(时间复杂度从 降至 )
使用示例
计算组合数 的函数:
long long nCr(int n, int k) {
if (k < 0 || k > n) return 0;
init(); // 确保已初始化
return fact[n] * inv_fact[k] % MOD * inv_fact[n - k] % MOD;
}
调用示例:
cout << nCr(5, 2); // 输出 C(5,2)=10 % MOD
复杂度分析
- 时间复杂度:(初始化仅需遍历两次数组)
- 空间复杂度:(存储两个长度为
MX的数组)
应用场景
- 组合数学问题(如排列组合计数)
- 概率与统计(多项式系数计算)
- 动态规划优化(将组合数计算从 降至 )
- 算法竞赛(如Codeforces/LeetCode中需要快速计算大组合数的问题)