卢卡斯定理
对于非负整数a,b和质数p,有Cba≡Cb mod pa mod p⋅C⌊b/p⌋⌊a/p⌋ (mod p)
证明1
引理
(1+x)pα≡1+xpα (mod p)
当α=0 时,显然成立。
当α=1时,有(1+x)p=C0px0+C1px1+C2px2+⋅⋅⋅+Cppxp其中Cip=p!(p−i)!⋅i!由于p是质数,所以在上式的分母中,不存在可以消去分子中为p的数,因此当i=1, 2, ..., p−1时,有Cip=p!(p−i)!⋅i!≡0 (mod p)因此(1+x)p=C0px0+C1px1+C2px2+⋅⋅⋅+Cppxp≡C0px0+Cppxp=1+xp (mod p)即(1+x)p≡1+xp (mod p)
数学归纳法。假设当(1+x)pα≡1+xpα (mod p)成立时,证明(1+x)pα+1≡1+xpα+1 (mod p)也成立。(1+x)pα+1=((1+x)pα)p≡(1+xpα)p (mod p)=C0p(xpα)0+C1p(xpα)1+C2p(xpα)2+⋯+Cpp(xpα)p≡1+xpα+1 (mod p)即(1+x)pα+1≡1+xpα+1 (mod p)引理证毕。
接下来我们把a和b转换为对应的p进制数,即a=akpk+ak−1pk−1+⋅⋅⋅+a0b=bkpk+bk−1pk−1+⋅⋅⋅+b0如果a和b在p进制下的位数不一样,那么就在位数小的前面补0,使得他们的位数是一样的。
接着我们有(1+x)a=(1+x)ak pk + ak−1 pk−1 + ⋅⋅⋅ + a0=((1+x)pk)ak⋅((1+x)pk−1)ak−1⋅ ⋅⋅⋅ ⋅((1+x)p0)a0≡(1+xpk)ak⋅(1+xpk−1)ak−1⋅ ⋅⋅⋅ ⋅(1+x)a0 (mod p)即(1+x)a≡(1+xpk)ak⋅(1+xpk−1)ak−1⋅ ⋅⋅⋅ ⋅(1+x)a0 (mod p)
我们要知道Cba的值,其实就是左式展开中的xb的系数。而在右式中,等价于要知道xbk pk + bk−1 pk−1 + ⋅⋅⋅ + b0的系数,即Cbkak⋅Cbk−1ak−1⋅ ⋅⋅⋅ ⋅Cb0a0其中,Cbkak为右式(1+xpk)ak中(xpk)bk的系数,简单来说可以类比成Cba是(1+x)a展开式中xb的系数,以此类推。因此有Cba≡Cbkak⋅Cbk−1ak−1⋅ ⋅⋅⋅ ⋅Cb0a0 (mod p)为了将上式转换为如下形式Cba≡Cb mod pa mod p⋅C⌊b/p⌋⌊a/p⌋ (mod p)我们先把Cbkak⋅Cbk−1ak−1⋅ ⋅⋅⋅ ⋅Cb0a0拆分成Cbkak⋅Cbk−1ak−1⋅ ⋅⋅⋅ ⋅Cb1a1和Cb0a0这两部分。
首先对于Cb0a0,其实它就等于Cb mod pa mod p,这是因为我们要得到某个数的p进制数,就要用p整除这个数,得到一个商和余数;再用p去除商,又得到一个商和余数,以此类推,直到商为小于p时为止。以a为例,把最先得到的余数作为p进制数的最低位有效位,也就是a0。我们又知道a可以表述为这种形式a=⌊ap⌋⋅p+r,这里的余数r正是对应于a0。以此类推b也一样。
然后就是Cbkak⋅Cbk−1ak−1⋅ ⋅⋅⋅ ⋅Cb1a1。试想一下,我们是通过把a,b转换为p进制a=akpk+ak−1pk−1+⋅⋅⋅+a0b=bkpk+bk−1pk−1+⋅⋅⋅+b0进而推出Cba≡Cbkak⋅Cbk−1ak−1⋅ ⋅⋅⋅ ⋅Cb0a0 (mod p)现在我们把a,b都右移1位,得到⌊ap⌋和⌊bp⌋,对应的p进制就是⌊ap⌋=akpk−1+ak−1pk−2+⋅⋅⋅+a1⌊bp⌋=bkpk−1+bk−1pk−2+⋅⋅⋅+b1用类比的方法,我们就可以得到Cbkak⋅Cbk−1ak−1⋅ ⋅⋅⋅ ⋅Cb1a1≡C⌊b/p⌋⌊a/p⌋ (mod p)事实上这是正确的,我们可以从C⌊b/p⌋⌊a/p⌋入手分析,方法与上面分析Cba的一样(这也暗示我们可以用递归来求解),同样会得到C⌊b/p⌋⌊a/p⌋≡Cbkak⋅Cbk−1ak−1⋅ ⋅⋅⋅ ⋅Cb1a1 (mod p)
因此,有Cba≡Cbkak⋅Cbk−1ak−1⋅ ⋅⋅⋅ ⋅Cb0a0≡Cb mod pa mod p⋅C⌊b/p⌋⌊a/p⌋ (mod p)定理得证。
证明2
这里再给出另一种证法。
我们设
{⌊a/p⌋=qa⌊b/p⌋=qb, {a mod p=rab mod p=rb所以有
{a=qa⋅p+rab=qb⋅p+rb根据二项式定理有(1+x)a=a∑k=0Cka⋅xk同时也有(1+x)a=(1+x)qa⋅p+ra=(1+x)qa⋅p⋅(1+x)ra≡(1+xp)qa⋅(1+x)ra (mod p)=qa∑i=0Ciqa⋅xi⋅p⋅ra∑j=0Cjra⋅xj=qa∑i=0ra∑j=0Ciqa⋅Cjra⋅xi⋅p+j即(1+x)a≡qa∑i=0ra∑j=0Ciqa⋅Cjra⋅xi⋅p+j (mod p)我们令x的指数为k=i⋅p+j,其中0≤i≤qa, 0≤j≤p−1,同时有0≤k≤a,所以可以把上式修改为从0枚举到a,i和j就变为i=⌊kp⌋+⌊jp⌋=⌊kp⌋j=k−i⋅p=k mod p所以就有(1+x)a≡a∑k=0C⌊k/p⌋qa⋅Ck mod pra⋅xk (mod p)综合上面,得到a∑k=0Cka⋅xk≡a∑k=0C⌊k/p⌋qa⋅Ck mod pra⋅xk (mod p)然后我们令k=b就会得到Cba≡C⌊b/p⌋qa⋅Cb mod pra (mod p)即Cba≡C⌊b/p⌋⌊a/p⌋⋅Cb mod pa mod p (mod p)定理得证。
卢卡斯定理代码
什么时候用卢卡斯定理呢,只要a的值大于p就需要用到卢卡斯定理了。如果p比a小,就不能保证a−b和b的逆元存在了,它们可能是p的倍数。
相关代码如下:
1 int qmi(int a, int k, int p) {
2 int ret = 1;
3 while (k) {
4 if (k & 1) ret = (long long)ret * a % p;
5 k >>= 1;
6 a = (long long)a * a % p;
7 }
8
9 return ret;
10 }
11
12 int C(int a, int b, int p) {
13 int ret = 1;
14 for (int i = 1, j = a; i <= b; i++, j--) {
15 ret = (long long)ret * j % p;
16 // 因为总是满足i < p且p为质数,所以应用费马小定理,用快速幂来求得在模p下i的逆元
17 ret = (long long)ret * qmi(i, p - 2, p) % p;
18 }
19
20 return ret;
21 }
22
23 int lucas(long long a, long long b, int p) {
24 if (a < p && b < p) return C(a, b, p);
25 return (long long)C(a % p, b % p, p) * lucas(a / p, b / p, p) % p;
26 }
参考资料
AcWing 887. 求组合数 III:https://www.acwing.com/video/308/
算法学习笔记(25): 卢卡斯定理:https://zhuanlan.zhihu.com/p/116698264
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!