1. 程式人生 > >Lucas定理學習筆記

Lucas定理學習筆記

Lucas定理:

(nm)(npmp)×(n%pm%p)mod  p{n\choose m}\equiv{\lfloor{\frac{n}{p}\rfloor}\choose \lfloor \frac{m}{p}\rfloor}\times{n\%p\choose m\%p} \mod p 此處的%\%表示的是取模運算。

證明:

考慮化簡(nm)=n!m!×(nm)!{n\choose m}=\frac{n!}{m!\times (n-m)!}

!×(nm)!n!,不難發現當n和m都遠大於p的時候為了簡化運算我們可以將n,m,(n-m)都給按照p分段,如果n%pm%pn\%p \geq m\%p,那麼可以發現以分數線為界,分數線上面的整數段一定和分數線下面的整數段相同,反之則分數線上面的整數段比下面的整數段大1(這種情況不難發現答案是0)。 於是我們只考慮上面和下面整數段相同的情況,先計算剩下來的邊角,根據同餘可得邊角料的部分為(n%pm%p){n\%p\choose m\%p}。 然後考慮對於這些整塊,要如何簡化運算。根據同餘定理和逆元的一些性質可以得到對於分子和分母對於p同餘且不是p的倍數的部分一定可以消掉,就像這樣: 1
×2××p×(p+1)×(p+2)××(t×p)1×2××(b×p)×1×2×((tb)×p)p×2p××tpp××bp×p×(tb)pmod  p\frac{1\times 2\times \dots \times p\times (p+1)\times (p+2)\times \cdots \times (t\times p)}{1\times 2\times \cdots \times (b\times p)\times 1\times 2\cdots\times ((t-b)\times p)}\\\equiv\frac{p\times 2p\times \cdots \times tp}{p\times \cdots\times bp\times p\times (t-b)p} \mod p
然後我們把分子分母同時除以一個ptp^t,就可以得到這部分的值為(npmp)\lfloor{\frac{n}{p}\rfloor}\choose \lfloor \frac{m}{p}\rfloor了。 最後可得(nm)(npmp)×(n%pm%p)mod  p{n\choose m}\equiv{\lfloor{\frac{n}{p}\rfloor}\choose \lfloor \frac{m}{p}\rfloor}\times{n\%p\choose m\%p} \mod p。 證畢。 以上內容均為自己的對於Lucas定理及其證明的淺解,如有錯誤,歡迎指正。

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define MREP(i,x) for(int i=beg[x],v;v=to[i],i;i=las[i])
#define debug(x) cout<<#x<<"="<<x<<endl
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;

using namespace std;

void File(){
    freopen("luogu3807.in","r",stdin);
    freopen("luogu3807.out","w",stdout);
}

template<typename T>void read(T &_){
    T __=0,mul=1; char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')mul=-1;
        ch=getchar();
    }
    while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
    _=__*mul;
}

const int maxn=1e5+10;
int T;
ll n,m,p,fac[maxn<<1];

ll qpow(ll x,ll y){
    ll ret=1; x%=p;
    while(y){
        if(y&1)ret=ret*x%p;
        x=x*x%p;
        y>>=1;
    }
    return ret;
}

ll calc(ll x,ll y){
    if(x<p && y<p){
        if(x<y)return 0;
        return fac[x]*qpow(fac[y],p-2)%p*qpow(fac[x-y],p-2)%p;
    }
    return calc(x/p,y/p)*calc(x%p,y%p)%p;
}

int main(){
//	File();
    fac[0]=1;
    read(T);
    while(T--){
        read(n),read(m),read(p);
        REP(i,1,n+m)fac[i]=fac[i-1]*i%p;
        printf("%lld\n",(calc(n+m,m)%p+p)%p);
    }
    return 0;
}