簡述
盧卡斯定理是用於求c(n,m) mod p,p為素數的值。
題目中求n和m很大的組合數時,結果一般都會溢出,所以經常會求組合數%p的某個值。當p大於m時,我們可以直接根據定義求分母在模p意義下的乘法逆元求出結果:
但當p<m時,分母的乘法逆元可能不存在(m可能是p的倍數),所以就輪到盧卡斯定理出場了。
定理描述
對於非負整數n,m和質數p
其中,
為n和m的p進制展開。
在做題中我們用到的是遞推式:
當m<n時,規定c(n,m)=0。
證明
設x是任意小於p的正整數,那么有:
因為x<p,所以x存在modp意義下的逆元,於是乎:
因為等式右邊是p的倍數,所以:
公式1
根據二項式定理,在modp意義下,我們有
當n=p時,根據公式1,除了i=0和i=p時,其余項都為0,所以
公式2
現在我們設,
,那么
根據二項式定理,我們有:
我們對等式左端進行變形:
也就是說
我們來看k=n的情況,觀察項,左邊為
,我們現在來看右邊,因為j<rm<p,同時rn<rm,我們只能取j=rn,i=qn
於是乎我們可以得到:
兩邊同時乘inv(xn),我們就可以得到:
代碼詳解
根據上面的遞推式,我們可以寫出如下代碼:
注意這里是n下m上,遞歸終點為m==0
ll lucas(ll n,ll m){ if(m==0) return 1; return lucas(n/p,m/p)*c(n%p,m%p)%p; }
那怎么算C(n,m)呢?我們就要用到乘法逆元,根據定義來算
這里的num[i]為i的階層,inv(i)為i在模p意義下的乘法逆元
ll c(ll n,ll m){ if(m>n) return 0; return (num[n]*inv(num[m])%p)*inv(num[n-m])%p; }
乘法逆元我們用費馬小定理來算,這里不展開敘述
ll mypow(ll x,ll n,ll m){ ll res=1; while(n){ if(n&1) res=res*x%m; x=x*x%m; n>>=1; } return res; } ll inv(ll x){ return mypow(x,p-2,p); }
模板

ll num[maxn]; ll mypow(ll x,ll n,ll m){ ll res=1; while(n){ if(n&1) res=res*x%m; x=x*x%m; n>>=1; } return res; } ll inv(ll x){ return mypow(x,p-2,p); } ll c(int n,int m){ if(m>n) return 0; return (num[n]*inv(num[m])%p)*inv(num[n-m])%p; } ll lucas(ll n,ll m){ if(m==0) return 1; return lucas(n/p,m/p)*c(n%p,m%p)%p; }