扩展卢卡斯定理(ExLucas)一命速通
扩展卢卡斯定理(ExLucas)一命速通
一、前置知识
先回顾普通卢卡斯定理(Lucas):
普通 Lucas 用于求
其中 \(p\) 是质数。
公式:
但如果 模数 \(p\) 不是质数,普通 Lucas 失效,这时就要用 ExLucas(扩展卢卡斯)。
二、ExLucas 解决的问题
求:
\(P\) 为任意正整数(不一定是质数),且 \(0 \le m \le n\)。
整体思路(三大步骤)
- 对模数 \(P\) 质因数分解
- 分别求解:
- 用 中国剩余定理(CRT) 合并所有同余方程,得到最终 \(x \bmod P\)。
核心难点:如何计算 \(\dbinom{n}{m} \bmod p^k\)(\(p\) 是质数,\(k \ge 1\))
三、核心:计算 \(\boldsymbol{\dbinom{n}{m} \bmod p^k}\)
组合数展开:
问题:\(m!、(n-m)!\) 可能和 \(p^k\) 不互质,不存在逆元,所以不能直接求逆。
处理方案
把阶乘里所有因子 \(p\) 全部提出来,拆分式子:
设:
其中 \(A,B,C\) 都与 \(p\) 互质。
则:
此时 \(\dfrac{A}{BC}\) 中分母和 \(p^k\) 互质,可以求逆元。
于是拆成两部分计算:
- 求指数:\(cnt = a-b-c\)(\(p\) 的幂次)
- 求剩余部分:\(\dfrac{A}{B \cdot C} \bmod p^k\)
1. 求阶乘中质因子 \(p\) 的个数(勒让德公式)
\(n!\) 里包含多少个因子 \(p\):
那么:
总幂次:
2. 计算 \(\boldsymbol{n!}\) 去掉所有 \(p\) 因子后 \(\boldsymbol{\bmod\ p^k}\)
定义函数:
即:\(n!\) 剔除全部 \(p\) 后的结果模 \(p^k\)。
推导公式
把 \(1 \sim n\) 分段:
提取每一段里的 \(p\):
对「不含 \(p\) 的部分」按模 \(p^k\) 分组:
每 \(p^k\) 个数为一个周期,周期内乘积可预处理/快速幂。
最终递归公式:
直观理解:
-
完整周期乘积快速幂
-
余下零散部分直接乘
-
剩下 \(\lfloor n/p \rfloor!\) 继续递归剔除因子 \(p\)
3. 组合数整合 \(\boldsymbol{\dbinom{n}{m} \bmod p^k}\)
- \(\mathrm{inv}(x,mod)\):\(x\) 在模 \(mod\) 下的乘法逆元(\(\gcd(x,mod)=1\),可用扩展欧几里得 / 费马)
四、中国剩余定理 CRT 合并结果
设分解后:
已求得:
因为所有 \(q_i\) 两两互质,用普通 CRT 合并得到唯一解:
这就是 \(\dbinom{n}{m} \bmod P\) 的最终答案。
五、完整算法流程总结
给定 \(n,m,P\),求 \(\dbinom{n}{m} \bmod P\):
-
分解 \(P\):\(P=\prod p_i^{k_i}\)
-
对每个模数 \(q=p^k\):
-
用勒让德公式算出 \(cnt = f(n,p)-f(m,p)-f(n-m,p)\)
-
递归求 \(g(n,p,q),\ g(m,p,q),\ g(n-m,p,q)\)
-
求后两者的逆元,算出:
\[res_i = p^{cnt} \cdot g(n) \cdot inv(g(m)) \cdot inv(g(n-m)) \pmod{q} \] -
-
CRT 合并 所有 \((res_i,q_i)\),得到最终答案。
六、简单示例(手算演示)
求 \(\dbinom{10}{3} \bmod 14\)
-
分解模数:\(14=2^1 \times 7^1\)
-
分别计算:
-
\(\dbinom{10}{3} \bmod 2\)
-
\(\dbinom{10}{3} \bmod 7\)
-
-
CRT 合并。
先算组合数原值:
解同余方程:设 \(x=7t+1\)
\(x=7\times 1+1=8\)
最终:
七、关键代码思路(C++/Python 通用框架)
必备函数
-
ll qpow(ll a,ll b,ll mod)快速幂 -
ll exgcd(ll a,ll b,ll &x,ll &y)扩展欧几里得(求逆元) -
ll inv(ll a,ll mod)求逆元 -
ll count_p(ll n,ll p)勒让德公式,统计 \(n!\) 中 \(p\) 个数 -
ll fact(ll n,ll p,ll pk)递归求 \(g(n,p,p^k)\) -
ll C_mod_pk(ll n,ll m,ll p,ll pk)计算 \(\dbinom{n}{m} \bmod p^k\) -
ll crt(vector<ll> a,vector<ll> m)中国剩余定理合并
主函数 ExLucas
-
质因数分解 \(P\) 得到所有 \(p^k\)
-
逐个计算组合数模 \(p^k\)
-
CRT 合并输出答案
八、适用范围 & 注意点
-
ExLucas 可以处理 模数为任意正整数 的组合数取模,是 Lucas 的通用扩展。
-
递归求 \(g(n,p,p^k)\) 是核心,时间复杂度依赖质因数大小和 \(k\)。
-
必须保证:计算逆元时,数与模数互质(前面剔除因子 \(p\) 就是为了满足这一点)。
-
若 \(m>n\),组合数为 \(0\)。
九、模版代码
#include <bits/stdc++.h>
using namespace std;
#define SHIH_SSU 0
#define ll long long
ll n, m, P;
ll mul(ll a, ll b, ll mod){
return (__int128)a * b % mod;
}
ll qpow(ll a, ll b, ll mod){
ll res = 1;
while(b){
if (b & 1) res = mul(res, a, mod);
a = mul(a, a, mod);
b >>= 1;
}
return res;
}
ll exgcd(ll a, ll b, ll &x, ll &y){
if(!b){
x = 1, y = 0;
return a;
}
ll d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
// 求逆元
ll inv(ll a, ll mod){
ll x, y;
exgcd(a, mod, x, y);
return (x % mod + mod) % mod;
}
// 计算 n! 中去掉质因子 p 后,模 p^k 的值
ll fact(ll n, ll p, ll pk){
if(!n) return 1;
ll res = 1;
// 一段内的乘积
for(ll i = 1; i <= pk; ++i)
if(i % p) res = mul(res, i, pk);
// 整段的快速幂
res = qpow(res, n / pk, pk);
// 剩余部分
for(ll i = 1; i <= n % pk; ++i)
if(i % p) res = mul(res, i, pk);
// 递归
return mul(res, fact(n / p, p, pk), pk);
}
// 计算 C(n,m) mod p^k
ll C(ll n, ll m, ll p, ll pk){
if (m > n) return 0;
ll f1 = fact(n, p, pk);
ll f2 = fact(m, p, pk);
ll f3 = fact(n - m, p, pk);
// 统计质因子 p 的个数
ll cnt = 0;
for(ll i = n; i; i /= p) cnt += i / p;
for(ll i = m; i; i /= p) cnt -= i / p;
for(ll i = n - m; i; i /= p) cnt -= i / p;
// 合并结果
ll ans = mul(f1, inv(f2, pk), pk);
ans = mul(ans, inv(f3, pk), pk);
ans = mul(ans, qpow(p, cnt, pk), pk);
return ans;
}
// 中国剩余定理
ll crt(vector<ll> a, vector<ll> m) {
ll M = 1, ans = 0;
for(ll x : m) M *= x;
for(int i = 0; i < (int)a.size(); i ++){
ll Mi = M / m[i];
ll t = inv(Mi, m[i]);
ans = (ans + mul(mul(a[i], Mi, M), t, M)) % M;
}
return (ans + M) % M;
}
// 扩展卢卡斯
ll exlucas(ll n, ll m, ll p){
vector<ll> a, mod;
for(ll i = 2; i * i <= p; ++i){
if(p % i == 0){
ll pk = 1;
while (p % i == 0){
pk *= i;
p /= i;
}
a.push_back(C(n, m, i, pk));
mod.push_back(pk);
}
}
if(p > 1){
a.push_back(C(n, m, p, p));
mod.push_back(p);
}
return crt(a, mod);
}
int main(){
cin >> n >> m >> P;
cout << exlucas(n, m, P) << endl;
return SHIH_SSU;
}
浙公网安备 33010602011771号