卢卡斯定理
卢卡斯定理(Lucas' Theorem)是组合数学中用于计算大组合数模素数的核心定理,其核心思想是将大组合数分解为小组合数的乘积。以下结合参考文献进行详细讲解:
定理内容
对于素数 和非负整数 ,有:
其中:
- 表示向下取整;
- 和 是模 的余数(范围在 )。
证明思路
证明基于二项式展开和模运算性质:
-
二项式展开: 考虑 。 由于 (费马小定理),右侧可化为 。
-
系数匹配: 左侧 的系数为 ,右侧需将 分解为 。 此时 的系数为:
等式成立。
-
边界处理:
- 若 ,则 ;
- 若 ,则 。
算法实现(C++ 代码)
#include <iostream>
using namespace std;
// 预处理阶乘及其逆元(用于小组合数计算)
const int MAX_P = 10000; // 假设 p < MAX_P
long long fact[MAX_P], inv_fact[MAX_P];
// 快速幂求逆元
long long qpow(long long a, long long b, long long p) {
long long res = 1;
while (b) {
if (b & 1) res = res * a % p;
a = a * a % p;
b >>= 1;
}
return res;
}
// 初始化阶乘表
void init(long long p) {
fact = 1;
for (int i = 1; i < p; i++)
fact[i] = fact[i - 1] * i % p;
inv_fact[p - 1] = qpow(fact[p - 1], p - 2, p); // 费马小定理求逆元
for (int i = p - 2; i >= 0; i--)
inv_fact[i] = inv_fact[i + 1] * (i + 1) % p;
}
// 计算小组合数 C(n, k) mod p(要求 n, k < p)
long long C_small(long long n, long long k, long long p) {
if (k < 0 || k > n) return 0;
return fact[n] * inv_fact[k] % p * inv_fact[n - k] % p;
}
// 卢卡斯定理递归计算
long long Lucas(long long n, long long k, long long p) {
if (k == 0) return 1; // 递归终止条件
long long n1 = n / p, k1 = k / p; // 整除部分
long long n2 = n % p, k2 = k % p; // 余数部分
if (k2 > n2) return 0; // 边界:余数组合数为0
return C_small(n2, k2, p) * Lucas(n1, k1, p) % p;
}
int main() {
long long n, k, p;
cin >> n >> k >> p;
init(p); // 预处理阶乘
cout << Lucas(n, k, p) << endl;
return 0;
}
关键点解析
-
递归分解: 将大组合数 递归分解为 和 ,直到 。
-
预处理优化:
- 预处理阶乘表
fact[]及其逆元inv_fact[],使小组合数计算 ; - 利用费马小定理求逆元(要求 是素数)。
- 预处理阶乘表
-
时间复杂度:
- 递归深度 ,每层计算 ;
- 预处理 ,总复杂度 ,适用于 较小的情况。
-
与 exLucas 的关系: 当模数非素数时需用 exLucas 算法,但模数为素数时两者结果等价。
注意事项
- 适用条件:仅当模数 为素数时成立;
- 边界处理:递归中需检查 ,否则组合数为 0。
此实现满足 OI/ACM 风格,兼顾效率与可读性。