组合数求模模板

参考链接

 

C(n, m) 代表 从 n 个物品中取 m 个的方案数

1、n ≤ 1e3 、m ≤ 1e3

利用组合递推公式打表就行了

const int C_maxn = 1e3 + 10;

LL Comb[maxn][maxn];

inline void Comb_init()
{
    for(int i=1; i<C_maxn; i++){
        Comb[i][0] = 1;
        Comb[i][i] = 1;
        for(int j=1; j<C_maxn; j++){
            Comb[i][j] = Comb[i][j-1] + Comb[i-1][j-1];
            //Comb[i][j] = (Comb[i][j-1] + Comb[i-1][j-1]) % mod;
        }
    }
}
View Code

2、n ≤ 1e6、m ≤ 1e6 、模数 mod 为素数的情况下

预处理阶乘极其逆元

const int Comb_Maxn = 1e5 + 10;

LL Fac_inv[Comb_Maxn];
LL Fac[Comb_Maxn];

inline void Comb_init()
{
    Fac_inv[0] = Fac[0] = 1;
    Fac_inv[1] = 1;

    for(int i=1; i<Comb_Maxn; i++)
        Fac[i] = Fac[i-1] * (LL)i % mod;

    for(int i=2; i<Comb_Maxn; i++)
        Fac_inv[i] = (LL)(mod - mod / i) * Fac_inv[mod % i] % mod;

    for(int i=1; i<Comb_Maxn; i++)
        Fac_inv[i] = Fac_inv[i-1] * Fac_inv[i] % mod;
}

LL Comb(int n, int m)
{ return Fac[n] * Fac_inv[m] % mod * Fac_inv[n-m] % mod; }
View Code

 

3、n ≤ 1e18、m ≤ 1e18 、模数 mod 为素数的情况下

利用卢卡斯定理

LL pow_mod(LL a, LL b, LL p)
{
    LL res = 1;
    while(b != 0){
        if(b&1) res = (res * a) % p;
        a = (a*a) % p;
        b >>= 1;
    }
    return res;
}

LL Comb(LL a, LL b, LL p)
{
    if(a < b)   return 0;
    if(a == b)  return 1;
    if(b > a - b)   b = a - b;

    LL ans = 1, ca = 1, cb = 1;
    for(LL i = 0; i < b; ++i)
    {
        ca = (ca * (a - i))%p;
        cb = (cb * (b - i))%p;
    }
    ans = (ca*pow_mod(cb, p - 2, p)) % p;
    return ans;
}

LL Lucas(LL n,LL m,LL p)
{
     LL ans = 1;

     while(n&&m&&ans){
        ans = (ans*Comb(n%p, m%p, p)) % p;
        n /= p;
        m /= p;
     }
     return ans;
}
View Code

如果 mod 数小一点、可预处理阶乘逆元

const int P_maxn = 10005;

bool isPrime[P_maxn];
int prime[P_maxn],fac[P_maxn][P_maxn],inv[P_maxn][P_maxn],cnt,pth[P_maxn];

void prime_init(){
    cnt=0;
    memset(isPrime,true,sizeof(isPrime));
    for(int i=2;i<P_maxn;i++){
        if(isPrime[i]){
            prime[++cnt]=i;
            pth[i]=cnt;
            for(int j=i+i;j<P_maxn;j+=i)
                isPrime[j]=0;
        }
    }
}

int quick_mod(int a, int b, int p){
    a %= p;
    int ret = 1;
    while(b){
        if(b & 1){
            ret = ret * a % p;
            b--;
        }
        b>>=1;
        a = a * a % p;
    }
    return ret;
}

void inv_init(){//预处理阶乘与逆元
    int i,j;
    for(int i=1;i<=cnt;i++){
        fac[i][0]=inv[i][0]=1;
        for(int j=1;j<prime[i];j++){
            fac[i][j] = (fac[i][j-1] * j) % prime[i];
            inv[i][j] = quick_mod(fac[i][j], prime[i]-2, prime[i]);
        }
    }
}

int Comb(int n,int m,int P){
    if(m>n) return 0;
    if(m==n) return 1;
    int t=pth[P];
    return fac[t][n]*(inv[t][n-m]*inv[t][m]%P)%P;
}

int Lucas(int n,int m,int P){
    if(m==0) return 1;
    return Comb(n%P, m%P, P) * Lucas(n/P, m/P, P)%P;
}
View Code

4、n ≤ 1e6、m ≤ 1e6 、模数 mod 可能为合数的情况下

const int P_maxn = 200005;

bool isPrime[P_maxn];
int prime[P_maxn];
int cnt;

void prime_init()
{
    cnt = 0;
    memset(isPrime,true,sizeof(isPrime));
    for(int i=2; i<P_maxn; i++)
    {
        if(isPrime[i])
        {
            prime[cnt++] = i;
            for(int j=i+i; j<P_maxn; j+=i)
                isPrime[j] = false;
        }
    }
}

LL quick_mod(LL a,LL b,LL m)
{
    LL ans = 1;
    a %= m;
    while(b)
    {
        if(b & 1)
        {
            ans = ans * a % m;
            b--;
        }
        b >>= 1;
        a = a * a % m;
    }
    return ans;
}

LL Work(LL n,LL p)
{
    LL ans = 0;
    while(n)
    {
        ans += n / p;
        n /= p;
    }
    return ans;
}

LL Solve(LL n,LL m,LL P)
{
    LL ans = 1;
    for(int i=0; i<cnt && prime[i]<=n; i++)
    {
        LL x = Work(n, prime[i]);
        LL y = Work(n - m, prime[i]);
        LL z = Work(m, prime[i]);
        x -= (y + z);
        ans *= quick_mod(prime[i], x, P);
        ans %= P;
    }
    return ans;
}

int main()
{
    prime_init();
    LL n,m,P;
    cin>>n>>m>>P;
    cout<<Solve(n,m,P)<<endl;
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/qwertiLH/p/9468855.html