数论:卢卡斯定理(求组合数)

点击查看折叠代码块
/*
C(n,m) % p = n!/(m! * (n-m)!) % p = n! % p * inv[m!] % p * inv[(n-m)!] % p

求出1-n的前缀积pre[1]--pre[n] 和 1-n的逆元的前缀积 pre_inv[1]--pre_inv[n]
则 C(n,m) = pre[n] % p * pre_inv[m] % p * pre_inv[n-m] % p

卢卡斯定理:
Lucas(n,0) = 1
Lucas(n,m) % p = Lucas(n/p,m/p) * C(n % p,m % p)
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6+10;
ll n,m,Mod;
int T;

ll mypow(ll a,ll b){
    ll inv = 1;
    while( b ){
        if( b&1 )  inv = inv*a%Mod;
        a = a*a%Mod;
        b >>= 1;
    }
    return inv;
}

ll inv[maxn];//逆元
ll pre[maxn],pre_inv[maxn];

void init(ll n){//线性求逆元
    inv[0]=inv[1]=1;
    for (int i=2;i<=n;i++){
        inv[i] = ((Mod-Mod/i) *inv[Mod % i]) % Mod;
    }
}

void init_pre(ll n){//求出1-n的前缀积和逆元前缀积
    pre[0] = pre_inv[0] = 1;
    for (int i=1;i<=n;i++){
        pre[i] = pre[i-1] * i % Mod;
        pre_inv[i] = pre_inv[i-1] * inv[i] % Mod;
    }
}

ll C(ll n,ll m){
    if(m>n) return 0;
    return pre[n] * pre_inv[m] % Mod * pre_inv[n-m] % Mod;//记得取模
}

ll Lucas(ll n,ll m){
    if(m==0) return 1;
    return Lucas(n/Mod,m/Mod) * C(n % Mod,m % Mod) % Mod;//记得取模
}

int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%lld%lld%lld",&n,&m,&Mod);
        init(n+m);
        init_pre(n+m);
        printf("%lld
",Lucas(n+m,n));
    }
    return 0;
}
你将不再是道具,而是成为人如其名的人
原文地址:https://www.cnblogs.com/wsl-lld/p/13393531.html