1. 程式人生 > >洛谷P3807盧卡斯定理

洛谷P3807盧卡斯定理

這是一道模板題

這裡是題目 洛谷P3807

盧卡斯定理及題目的闡釋

盧卡斯定理是用來解決一大很大的組合數來和一個質數求餘的問題,它的定義如下
如果p為素數,設n=sp+q,m=tp+r
則:

那麼來看看題。
首先看這個資料範圍,就知道可以用int的型別解決個屁,就是int害的我錯了好多次long long 的資料型別解決,這個資料看起來好像是隻有 10的5次方,但是中間這麼多的計算,突然的就給我溢位了我靠著ZFN大佬的資料測試發現負數才發現的

long long C(int n,int m,int p){
    if(n<m)return 0;
    if(n==m)return 1;
    if(m>n-m)m=n-m;//約掉 
    long long s1=1,s2=1;
    for(int i=0;i<m;i++){
        s1=s1*(n-i)%p;
        s2=s2*(i+1)%p;
    }
    return s1*qkpow(s2,p-2,p)%p;
}

這一段是求組合的函式,拿出來單獨講一講
因為這裡面的引數傳過來的時候都是已經和p求過餘了的,而且p在題目中說了的,是一個質數所以說這裡的n,m都是和p互質的。
因為公式裡面要除掉m!,同時有要去對p取模,所以考慮用它的逆元乘來代替用它來除
而根據費馬小定理可以知道它的逆元是它的p-2

程式碼

那麼綜上,加上以個快速冪就可以解出這道題了

//洛谷P3807 Lucas 模板題 
#include<bits/stdc++.h>
#define maxn 100005
using namespace std;
int n,m,p;
long long qkpow(long long b,int p,int mod){
    long long res=1;
    while(p){
        if(p&1){
            (res*=b)%=mod;
        }
        (b*=b)%=mod;
        p>>=1;
    }
    return res;
}
long long C(int n,int m,int p){
    if(n<m)return 0;
    if(n==m)return 1;
    if(m>n-m)m=n-m;//約掉 
    long long s1=1,s2=1;
    for(int i=0;i<m;i++){
        s1=s1*(n-i)%p;
        s2=s2*(i+1)%p;
    }
    return s1*qkpow(s2,p-2,p)%p;
}
long long Lucas(int n,int m,int p){
    if(m==0)return 1;
    return C(n%p,m%p,p)*Lucas(n/p,m/p,p)%p;
}
int main(){
    #ifndef ONLINE_JUDGE
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
    #endif
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d%d",&n,&m,&p);
        n+=m;
        cout<<Lucas(n,m,p)<<"\n";
    }
    return 0;
}