hdu 3037 费马小定理+逆元除法取模+Lucas定理

组合数学推推推最后,推得要求C(n+m,m)%p

其中n,m小于10^9,p小于1^5

用Lucas定理求(Lucas定理求nm较大时的组合数)

因为p数据较小可以直接阶乘打表求逆元

求逆元时,由费马小定理知道p为素数时,a^p-1=1modp可以写成a*a^p-2=1modp

所以a的逆元就是a^p-2,

可以求组合数C(n,m)%p中除法取模,将其转化为乘法取模
即    n!/(m!*(n-m)!)=n!*(m!*(n-m)!)^p-2

求C(n+m,m)。

n,m<=1000,二维数组递推。

n,m<=1000000,一维数组预处理出阶乘。

Lucas适用于n,m较大,MOD较小的情况。

#include <iostream>
#define MAX 100005

typedef long long ll;
using namespace std;

ll mul[MAX],MOD;

void init(){
    mul[0]=1;
    for(int i=1;i<=MOD;i++){
        mul[i]=mul[i-1]*i%MOD;
    }
}
ll qMod(ll a,ll b){
    ll ans=1;
    a%=MOD;
    while(b){
        if(b&1) ans=ans*a%MOD;
        b>>=1;
        a=a*a%MOD;
    }
    return ans;
}
ll C(ll a,ll b){
    if(a<b) return 0;
    return mul[a]*qMod(mul[b]*mul[a-b],MOD-2)%MOD;
}
ll Lucas(ll a,ll b){
    if(b==0) return 1;
    return (C(a%MOD,b%MOD)*Lucas(a/MOD,b/MOD))%MOD;
}
int main()
{
    int T;
    ll n,m;
    cin>>T;
    while(T--){
        cin>>n>>m>>MOD;
        init();
        cout<<Lucas(n+m,m)<<endl;
    }
    return 0;
}
原文地址:https://www.cnblogs.com/yzm10/p/8763194.html