盧卡斯定理詳解


簡述

  盧卡斯定理是用於求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;
}
View Code

 

  

  

   

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

   

 

   

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM