卢卡斯定理
前置知识:阶乘取模
引入
本文讨论大组合数取模的求解.组合数,又称二项式系数,指表达式:
(𝑛𝑘)=𝑛!𝑘!(𝑛−𝑘)!.(nk)=n!k!(n−k)!.
规模不大时,组合数可以通过 递推公式 求解,时间复杂度为 𝑂(𝑛𝑘)O(nk);也可以在较大的素数模数 𝑝 >𝑛p>n
下,通过计算分子和分母的阶乘在 𝑂(𝑛)O(n)
时间内求解.但当问题规模很大(𝑛 ∼1018n∼1018
)时,这些方法不再适用.
基于 Lucas 定理及其推广,本文讨论一种可以在模数不太大 (𝑚 ∼106m∼106) 时求解组合数的方法.更准确地说,只要模数的唯一分解 𝑚 =∏𝑝𝑒𝑖𝑖m=∏piei
中所有素数幂的和(即 ∑𝑝𝑒𝑖𝑖∑piei
)在 106106
规模时就可以使用该方法,因为算法的预处理大致相当于这一规模.
Lucas 定理
首先讨论模数为素数 𝑝p 的情形.此时,有 Lucas 定理:
Lucas 定理
对于素数 𝑝p,有
(𝑛𝑘)≡(⌊𝑛/𝑝⌋⌊𝑘/𝑝⌋)(𝑛mod𝑝𝑘mod𝑝)(mod𝑝).(nk)≡(⌊n/p⌋⌊k/p⌋)(nmodpkmodp)(modp).
其中,当 𝑛 <𝑘n<k 时,二项式系数 (𝑛𝑘)(nk)
规定为 00
.
利用生成函数证明
考虑 (𝑝𝑛)mod𝑝(pn)modp 的取值.因为
(𝑝𝑛)=𝑝!𝑛!(𝑝−𝑛)!,(pn)=p!n!(p−n)!,
所以,当 𝑛 ≠0,𝑝n≠0,p 时,分母中都没有因子 𝑝p
,但分子中有因子 𝑝p
,所以分式一定是 𝑝p
的倍数,模 𝑝p
的余数是 00
;当 𝑛 =0,𝑝n=0,p
时,分式就是 11
.因此,
记 𝑓(𝑥) =𝑎𝑥𝑛 +𝑏𝑥𝑚f(x)=axn+bxm.一般地,由 二项式展开 和 费马小定理 有
(𝑓(𝑥))𝑝=(𝑎𝑥𝑛+𝑏𝑥𝑚)𝑝=𝑝∑𝑘=0(𝑝𝑘)(𝑎𝑥𝑛)𝑘(𝑏𝑥𝑚)𝑝−𝑘≡𝑎𝑝𝑥𝑝𝑛+𝑏𝑝𝑥𝑝𝑚≡𝑎(𝑥𝑝)𝑛+𝑏(𝑥𝑝)𝑚=𝑓(𝑥𝑝)(mod𝑝).(f(x))p=(axn+bxm)p=∑k=0p(pk)(axn)k(bxm)p−k≡apxpn+bpxpm≡a(xp)n+b(xp)m=f(xp)(modp).
其中,第三行的同余利用了前文说明的结论,即只有 𝑘 =0,𝑝k=0,p 时,组合数才不是 𝑝p
的倍数.
利用这一结论,考察二项式展开:
(1+𝑥)𝑛=(1+𝑥)𝑝⌊𝑛/𝑝⌋(1+𝑥)𝑛mod𝑝≡(1+𝑥𝑝)⌊𝑛/𝑝⌋(1+𝑥)𝑛mod𝑝(mod𝑝).(1+x)n=(1+x)p⌊n/p⌋(1+x)nmodp≡(1+xp)⌊n/p⌋(1+x)nmodp(modp).
等式左侧中,项 𝑥𝑘xk 的系数为
(𝑛𝑘)mod𝑝.(nk)modp.
转而计算等式右侧中项 𝑥𝑘xk 的系数.第一个因子中各项的次数必然是 𝑝p
的倍数,第二个因子中各项的次数必然小于 𝑝p
,而 𝑘k
分解成这样两部分的和的方式是唯一的,即带余除法:𝑘 =𝑝⌊𝑘/𝑝⌋ +(𝑘mod𝑝)k=p⌊k/p⌋+(kmodp)
.因此,第一个因子只能贡献其 𝑝⌊𝑘/𝑝⌋p⌊k/p⌋
次项,第二个因子只能贡献其 𝑘mod𝑝kmodp
次项.所以,右侧等式中 𝑥𝑘xk
系数为两个因子各自贡献的项的系数的乘积:
(⌊𝑛/𝑝⌋⌊𝑘/𝑝⌋)(𝑛mod𝑝𝑘mod𝑝)mod𝑝.(⌊n/p⌋⌊k/p⌋)(nmodpkmodp)modp.
令两侧系数相等,就得到 Lucas 定理.
利用阶乘取模的结论证明
此处提供一种基于 阶乘取模 相关结论的证明方法,以方便和后文 exLucas 部分的方法建立联系.已知二项式系数
(𝑛𝑘)=𝑛!𝑘!(𝑛−𝑘)!.(nk)=n!k!(n−k)!.
将阶乘 𝑛!n! 中 𝑝p
的幂次和其他因子分离,得到分解:
𝑛!=𝑝𝜈𝑝(𝑛!)(𝑛!)𝑝.n!=pνp(n!)(n!)p.
就得到二项式系数的表达式:
(𝑛𝑘)=𝑝𝜈𝑝(𝑛!)−𝜈𝑝(𝑘!)−𝜈𝑝((𝑛−𝑘)!)(𝑛!)𝑝(𝑘!)𝑝((𝑛−𝑘)!)𝑝.(nk)=pνp(n!)−νp(k!)−νp((n−k)!)(n!)p(k!)p((n−k)!)p.
幂次 𝜈𝑝(𝑛!)νp(n!) 和阶乘余数 (𝑛!)𝑝mod𝑝(n!)pmodp
都有递推公式:
𝜈𝑝(𝑛!)=⌊𝑛/𝑝⌋+𝜈𝑝(⌊𝑛/𝑝⌋!),(𝑛!)𝑝≡(−1)⌊𝑛/𝑝⌋⋅(𝑛mod𝑝)!⋅(⌊𝑛/𝑝⌋!)𝑝(mod𝑝).νp(n!)=⌊n/p⌋+νp(⌊n/p⌋!),(n!)p≡(−1)⌊n/p⌋⋅(nmodp)!⋅(⌊n/p⌋!)p(modp).
前者是 Legendre 公式的推论,后者是 Wilson 定理的推论.
将递推公式代入二项式系数的表达式并整理,就得到:
(𝑛𝑘)≡(−𝑝)⌊𝑛/𝑝⌋−⌊𝑘/𝑝⌋−⌊(𝑛−𝑘)/𝑝⌋⋅(𝑛mod𝑝)!(𝑘mod𝑝)!((𝑛−𝑘)mod𝑝)!⋅𝑝𝜈𝑝(⌊𝑛/𝑝⌋!)−𝜈𝑝(⌊𝑘/𝑝⌋!)−𝜈𝑝(⌊(𝑛−𝑘)/𝑝⌋!)(⌊𝑛/𝑝⌋!)𝑝(⌊𝑘/𝑝⌋!)𝑝(⌊(𝑛−𝑘)/𝑝⌋!)𝑝(mod𝑝).(nk)≡(−p)⌊n/p⌋−⌊k/p⌋−⌊(n−k)/p⌋⋅(nmodp)!(kmodp)!((n−k)modp)!⋅pνp(⌊n/p⌋!)−νp(⌊k/p⌋!)−νp(⌊(n−k)/p⌋!)(⌊n/p⌋!)p(⌊k/p⌋!)p(⌊(n−k)/p⌋!)p(modp).
现在考察 ⌊𝑛/𝑝⌋ −⌊𝑘/𝑝⌋ −⌊(𝑛 −𝑘)/𝑝⌋⌊n/p⌋−⌊k/p⌋−⌊(n−k)/p⌋ 的取值.因为有
𝑛=⌊𝑛/𝑝⌋𝑝+(𝑛mod𝑝),𝑘=⌊𝑘/𝑝⌋𝑝+(𝑘mod𝑝),𝑛−𝑘=⌊(𝑛−𝑘)/𝑝⌋𝑝+((𝑛−𝑘)mod𝑝),n=⌊n/p⌋p+(nmodp),k=⌊k/p⌋p+(kmodp),n−k=⌊(n−k)/p⌋p+((n−k)modp),
所以,利用第一式减去后两式,就得到
(⌊𝑛/𝑝⌋−⌊𝑘/𝑝⌋−⌊(𝑛−𝑘)/𝑝⌋)𝑝=(𝑘mod𝑝)+((𝑛−𝑘)mod𝑝)−(𝑛mod𝑝).(⌊n/p⌋−⌊k/p⌋−⌊(n−k)/p⌋)p=(kmodp)+((n−k)modp)−(nmodp).
等式右侧,前两项的和严格小于 2𝑝2p,而第三项 𝑛mod𝑝nmodp
正是前两项的和的余数,所以右侧必然非负,但小于 2𝑝2p
,又需要是 𝑝p
的倍数,就只能是 00
或 𝑝p
.这说明 ⌊𝑛/𝑝⌋ −⌊𝑘/𝑝⌋ −⌊(𝑛 −𝑘)/𝑝⌋⌊n/p⌋−⌊k/p⌋−⌊(n−k)/p⌋
只能是 00
或 11
:
- 如果它是 00
,那么此时也成立 (𝑛mod𝑝) =(𝑘mod𝑝) +((𝑛 −𝑘)mod𝑝)(nmodp)=(kmodp)+((n−k)modp)
.因此,上式中的第一个因子的指数为 00
,该因子就等于一;第二个因子就是 (𝑛mod𝑝𝑘mod𝑝)(nmodpkmodp)
;第三个因子则由前文的展开式可知,就等于 (⌊𝑛/𝑝⌋⌊𝑘/𝑝⌋)(⌊n/p⌋⌊k/p⌋)
.此时,Lucas 公式成立;
- 如果它是 11
,那么第一个因子的指数为 11
,该因子就等于零,所以二项式系数的余数为零.同时,Lucas 定理所要证明的等式右侧的 (𝑛mod𝑝𝑘mod𝑝)(nmodpkmodp)
也必然是零,因为此时必然有 (𝑛mod𝑝) <(𝑘mod𝑝)(nmodp)<(kmodp)
;否则,将有
((𝑛−𝑘)mod𝑝)=𝑝+(𝑛mod𝑝)−(𝑘mod𝑝)≥𝑝.((n−k)modp)=p+(nmodp)−(kmodp)≥p.
这显然与余数的定义矛盾.
综合两种情形,就得到了所要求证的 Lucas 定理.这一证明说明,在求解素数模下组合数时,利用 Lucas 定理和利用 exLucas 算法得到的结果是等价的.
Lucas 定理指出,模数为素数 𝑝p 时,大组合数的计算可以转化为规模更小的组合数的计算.在右式中,第一个组合数可以继续递归,直到 𝑛,𝑘 <𝑝n,k<p
为止;第二个组合数则可以直接计算,或者提前预处理出来.写成代码的形式就是:
示意
---|---
其中,`C(n, k, p)` 用于计算小规模的组合数.
递归至多进行 𝑂(log𝑝𝑛)O(logpn) 次,因而算法的复杂度为 𝑂(𝑓(𝑝) +𝑔(𝑝)log𝑝𝑛)O(f(p)+g(p)logpn),其中,𝑓(𝑝)f(p) 为预处理组合数的复杂度,𝑔(𝑝)g(p) 为单次计算组合数的复杂度.
### 参考实现
此处给出的参考实现在 𝑂(𝑝)O(p) 时间内预处理 𝑝p 以内的阶乘及其逆元后,可以在 𝑂(1)O(1) 时间内计算单个组合数:
参考实现
---|---
该实现的时间复杂度为 𝑂(𝑝 +𝑇log𝑝𝑛)O(p+Tlogpn),其中,𝑇T
为询问次数.
exLucas 算法
Lucas 定理中对于模数 𝑝p 要求必须为素数,那么对于 𝑝p
不是素数的情况,就需要用到 exLucas 算法.虽然名字如此,该算法实际操作时并没有用到 Lucas 定理.它的关键步骤是 计算素数幂模下的阶乘.上文的第二个证明指出了它与 Lucas 定理的联系.
素数幂模的情形
首先考虑模数为素数幂 𝑝𝛼pα 的情形.将阶乘 𝑛!n!
中的 𝑝p
的幂次和其他幂次分开,可以得到分解:
𝑛!=𝑝𝜈𝑝(𝑛!)(𝑛!)𝑝.n!=pνp(n!)(n!)p.
其中,𝜈𝑝(𝑛!)νp(n!) 为 𝑛!n!
的素因数分解中 𝑝p
的幂次,而 (𝑛!)𝑝(n!)p
显然与 𝑝p
互素.因此,组合数可以写作:
(𝑛𝑘)=𝑝𝜈𝑝(𝑛!)−𝜈𝑝(𝑘!)−𝜈𝑝((𝑛−𝑘)!)(𝑛!)𝑝(𝑘!)𝑝((𝑛−𝑘)!)𝑝.(nk)=pνp(n!)−νp(k!)−νp((n−k)!)(n!)p(k!)p((n−k)!)p.
式子中的 𝜈𝑝(𝑛!)νp(n!) 等可以通过 Legendre 公式 计算,(𝑛!)𝑝(n!)p
等则可以通过 递推关系 计算.因为后者与 𝑝𝛼pα
互素,所以分母上的乘积的逆元可以通过 扩展欧几里得算法 计算.问题就得以解决.
注意,如果幂次 𝜈𝑝(𝑛!) −𝜈𝑝(𝑘!) −𝜈𝑝((𝑛 −𝑘)!) ≥𝛼νp(n!)−νp(k!)−νp((n−k)!)≥α,余数一定为零,不必再做更多计算.
一般模数的情形
对于 𝑚m 是一般的合数的情形,只需要首先对它做 素因数分解:
𝑚=𝑝𝛼11𝑝𝛼22⋯𝑝𝛼𝑠𝑠.m=p1α1p2α2⋯psαs.
然后,分别计算出模 𝑝𝛼𝑖𝑖piαi 下组合数 (𝑛𝑘)(nk)
的余数,就得到 𝑠s
个同余方程:
⎧{ { { { {⎨{ { { { {⎩(𝑛𝑘)≡𝑟1,(mod𝑝𝛼11),(𝑛𝑘)≡𝑟2,(mod𝑝𝛼22),⋯(𝑛𝑘)≡𝑟𝑠,(mod𝑝𝛼𝑠𝑠).{(nk)≡r1,(modp1α1),(nk)≡r2,(modp2α2),⋯(nk)≡rs,(modpsαs).
最后,利用 中国剩余定理 求出模 𝑚m 的余数.
参考实现
最后,给出模板题目 二项式系数 的参考实现.
参考实现
---|---
该算法在预处理时将模数 𝑚m 分解为素数幂,然后对所有 𝑝𝛼pα 预处理了自 11 至 𝑝𝛼pα 所有非 𝑝p 倍数的自然数的乘积,以及它在中国剩余定理合并答案时对应的系数.预处理的时间复杂度为 𝑂(√𝑚 +∑𝑖𝑝𝛼𝑖𝑖)O(m+∑ipiαi).每次询问时,复杂度为 𝑂(log𝑚 +∑𝑖log𝑝𝑖𝑛)O(logm+∑ilogpin),复杂度中的两项分别是计算逆元和计算幂次、阶乘余数的复杂度.
## 习题
* [Luogu3807【模板】卢卡斯定理](https://www.luogu.com.cn/problem/P3807)
* [SDOI2010 古代猪文 卢卡斯定理](https://loj.ac/problem/10229)
* [Luogu4720【模板】扩展卢卡斯](https://www.luogu.com.cn/problem/P4720)
* [Ceizenpok’s formula](http://codeforces.com/gym/100633/problem/J)
* * *
> __本页面最近更新: 2026/1/7 08:56:54,[更新历史](https://github.com/OI-wiki/OI-wiki/commits/master/docs/math/number-theory/lucas.md)
> __发现错误?想一起完善?[在 GitHub 上编辑此页!](https://oi-wiki.org/edit-landing/?ref=/math/number-theory/lucas.md "edit.link.title")
> __本页面贡献者:[Enter-tainer](https://github.com/Enter-tainer), [c-forrest](https://github.com/c-forrest), [GitPinkRabbit](https://github.com/GitPinkRabbit), [Great-designer](https://github.com/Great-designer), [TonyYin0418](https://github.com/TonyYin0418), [Xeonacid](https://github.com/Xeonacid), [EntropyIncreaser](https://github.com/EntropyIncreaser), [ksyx](https://github.com/ksyx), [MegaOwIer](https://github.com/MegaOwIer), [sshwy](https://github.com/sshwy), [Henry-ZHR](https://github.com/Henry-ZHR), [iamtwz](https://github.com/iamtwz), [ouuan](https://github.com/ouuan), [Sheng-Horizon](https://github.com/Sheng-Horizon), [Tiphereth-A](https://github.com/Tiphereth-A), [CornWorld](https://github.com/CornWorld), [IceySakura](https://github.com/IceySakura), [Ir1d](https://github.com/Ir1d), [LuoYisu](https://github.com/LuoYisu), [Marcythm](https://github.com/Marcythm), [megakite](https://github.com/megakite), [Menci](https://github.com/Menci), [shawlleyw](https://github.com/shawlleyw), [StudyingFather](https://github.com/StudyingFather), [whongzhong](https://github.com/whongzhong), [YOYO-UIAT](https://github.com/YOYO-UIAT)
> __本页面的全部内容在**[CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/deed.zh) 和 [SATA](https://github.com/zTrix/sata-license)** 协议之条款下提供,附加条款亦可能应用