Shih_Ssu

导航

扩展卢卡斯定理(ExLucas)一命速通

扩展卢卡斯定理(ExLucas)一命速通

一、前置知识

先回顾普通卢卡斯定理(Lucas)
普通 Lucas 用于求

\[\binom{n}{m} \bmod p \]

其中 \(p\) 是质数

公式:

\[\binom{n}{m} \equiv \binom{\lfloor n/p \rfloor}{\lfloor m/p \rfloor} \cdot \binom{n \bmod p}{m \bmod p} \pmod p \]

但如果 模数 \(p\) 不是质数,普通 Lucas 失效,这时就要用 ExLucas(扩展卢卡斯)


二、ExLucas 解决的问题

求:

\[\binom{n}{m} \bmod P \]

\(P\) 为任意正整数(不一定是质数),且 \(0 \le m \le n\)

整体思路(三大步骤)

  1. 对模数 \(P\) 质因数分解

\[P = p_1^{k_1} \cdot p_2^{k_2} \cdots p_t^{k_t} \]

  1. 分别求解:

\[\begin{cases} x \equiv \dbinom{n}{m} \pmod{p_1^{k_1}}\\ x \equiv \dbinom{n}{m} \pmod{p_2^{k_2}}\\ \quad\vdots\\ x \equiv \dbinom{n}{m} \pmod{p_t^{k_t}} \end{cases} \]

  1. 中国剩余定理(CRT) 合并所有同余方程,得到最终 \(x \bmod P\)

核心难点:如何计算 \(\dbinom{n}{m} \bmod p^k\)\(p\) 是质数,\(k \ge 1\)


三、核心:计算 \(\boldsymbol{\dbinom{n}{m} \bmod p^k}\)

组合数展开:

\[\binom{n}{m} = \frac{n!}{m! \cdot (n-m)!} \]

问题:\(m!、(n-m)!\) 可能和 \(p^k\) 不互质,不存在逆元,所以不能直接求逆。

处理方案

把阶乘里所有因子 \(p\) 全部提出来,拆分式子:

设:

\[n! = p^{a} \cdot A,\quad m! = p^{b} \cdot B,\quad (n-m)! = p^{c} \cdot C \]

其中 \(A,B,C\) 都与 \(p\) 互质。

则:

\[\binom{n}{m} = \frac{p^a \cdot A}{p^b \cdot B \cdot p^c \cdot C} = p^{\,a-b-c} \cdot \frac{A}{B \cdot C} \]

此时 \(\dfrac{A}{BC}\) 中分母和 \(p^k\) 互质,可以求逆元

于是拆成两部分计算:

  1. 求指数:\(cnt = a-b-c\)\(p\) 的幂次)
  2. 求剩余部分:\(\dfrac{A}{B \cdot C} \bmod p^k\)

1. 求阶乘中质因子 \(p\) 的个数(勒让德公式)

\(n!\) 里包含多少个因子 \(p\)

\[f(n,p) = \left\lfloor \frac{n}{p} \right\rfloor + \left\lfloor \frac{n}{p^2} \right\rfloor + \left\lfloor \frac{n}{p^3} \right\rfloor + \cdots \]

那么:

\[a=f(n,p),\ b=f(m,p),\ c=f(n-m,p) \]

总幂次:

\[cnt = f(n,p)-f(m,p)-f(n-m,p) \]


2. 计算 \(\boldsymbol{n!}\) 去掉所有 \(p\) 因子后 \(\boldsymbol{\bmod\ p^k}\)

定义函数:

\[g(n,p,p^k) = \frac{n!}{p^{f(n,p)}} \pmod{p^k} \]

即:\(n!\) 剔除全部 \(p\) 后的结果模 \(p^k\)

推导公式

\(1 \sim n\) 分段:

\[\begin{aligned} n! &= \big(1\cdot 2\cdots (p-1)\cdot p \big) \cdot \big((p+1)\cdots (2p-1)\cdot 2p\big) \cdots \cdot \big((\lfloor n/p \rfloor p+1)\cdots n\big) \end{aligned} \]

提取每一段里的 \(p\)

\[n! = p^{\lfloor n/p \rfloor} \cdot \underbrace{\big(1\cdot2\cdots(p-1)\big) \big((p+1)\cdots(2p-1)\big)\cdots}_{\text{不含}p\text{的部分}} \cdot \big(\lfloor n/p \rfloor\big)! \]

对「不含 \(p\) 的部分」按模 \(p^k\) 分组:
\(p^k\) 个数为一个周期,周期内乘积可预处理/快速幂。

最终递归公式:

\[g(n,p,p^k) = \left[ \big(g(p^k-1,p,p^k)\big)^{\left\lfloor \frac{n}{p^k} \right\rfloor} \cdot g(n \bmod p^k,\,p,\,p^k) \right] \cdot g\big(\lfloor \tfrac{n}{p} \rfloor,\,p,\,p^k\big) \pmod{p^k} \]

直观理解:

  1. 完整周期乘积快速幂

  2. 余下零散部分直接乘

  3. 剩下 \(\lfloor n/p \rfloor!\) 继续递归剔除因子 \(p\)


3. 组合数整合 \(\boldsymbol{\dbinom{n}{m} \bmod p^k}\)

\[\binom{n}{m} \equiv p^{\,cnt} \cdot \Big( g(n,p,p^k) \cdot \mathrm{inv}\big(g(m,p,p^k),\ p^k\big) \cdot \mathrm{inv}\big(g(n-m,p,p^k),\ p^k\big) \Big) \pmod{p^k} \]

  • \(\mathrm{inv}(x,mod)\)\(x\) 在模 \(mod\) 下的乘法逆元\(\gcd(x,mod)=1\),可用扩展欧几里得 / 费马)

四、中国剩余定理 CRT 合并结果

设分解后:

\[P = \prod_{i=1}^t q_i,\quad q_i=p_i^{k_i} \]

已求得:

\[ans \equiv a_i \pmod{q_i},\quad i=1,2,\dots,t \]

因为所有 \(q_i\) 两两互质,用普通 CRT 合并得到唯一解:

\[ans \bmod P \]

这就是 \(\dbinom{n}{m} \bmod P\) 的最终答案。


五、完整算法流程总结

给定 \(n,m,P\),求 \(\dbinom{n}{m} \bmod P\)

  1. 分解 \(P\)\(P=\prod p_i^{k_i}\)

  2. 对每个模数 \(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} \]

  3. CRT 合并 所有 \((res_i,q_i)\),得到最终答案。


六、简单示例(手算演示)

\(\dbinom{10}{3} \bmod 14\)

  1. 分解模数:\(14=2^1 \times 7^1\)

  2. 分别计算:

    • \(\dbinom{10}{3} \bmod 2\)

    • \(\dbinom{10}{3} \bmod 7\)

  3. CRT 合并。

先算组合数原值:

\[\binom{10}{3}=120 \]

\[\begin{cases} x \equiv 0 \pmod 2\\ x \equiv 1 \pmod 7 \end{cases} \]

解同余方程:设 \(x=7t+1\)

\[7t+1 \equiv 0 \pmod 2 \Rightarrow t+1\equiv0\pmod2 \Rightarrow t=1 \]

\(x=7\times 1+1=8\)

最终:

\[\binom{10}{3} \bmod 14 = 8 \]


七、关键代码思路(C++/Python 通用框架)

必备函数

  1. ll qpow(ll a,ll b,ll mod) 快速幂

  2. ll exgcd(ll a,ll b,ll &x,ll &y) 扩展欧几里得(求逆元)

  3. ll inv(ll a,ll mod) 求逆元

  4. ll count_p(ll n,ll p) 勒让德公式,统计 \(n!\)\(p\) 个数

  5. ll fact(ll n,ll p,ll pk) 递归求 \(g(n,p,p^k)\)

  6. ll C_mod_pk(ll n,ll m,ll p,ll pk) 计算 \(\dbinom{n}{m} \bmod p^k\)

  7. ll crt(vector<ll> a,vector<ll> m) 中国剩余定理合并

主函数 ExLucas

  1. 质因数分解 \(P\) 得到所有 \(p^k\)

  2. 逐个计算组合数模 \(p^k\)

  3. CRT 合并输出答案


八、适用范围 & 注意点

  1. ExLucas 可以处理 模数为任意正整数 的组合数取模,是 Lucas 的通用扩展。

  2. 递归求 \(g(n,p,p^k)\) 是核心,时间复杂度依赖质因数大小和 \(k\)

  3. 必须保证:计算逆元时,数与模数互质(前面剔除因子 \(p\) 就是为了满足这一点)。

  4. \(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;
}

posted on 2026-06-07 16:31  Shih_Ssu  阅读(11)  评论(0)    收藏  举报