求解组合数(模板)

在这里插入图片描述

#include<cstdio>
const int N = 200000 + 5;
const int MOD = (int)1e9 + 7;
int F[N], Finv[N], inv[N];//F是阶乘,Finv是逆元的阶乘 
void init()
{
    inv[1] = 1;
    for(int i = 2; i < N; i ++){
        inv[i] = (MOD - MOD / i) * 1ll * inv[MOD % i] % MOD;//*1ll是将int转化为long long
    }
    F[0] = Finv[0] = 1;
    for(int i = 1; i < N; i ++){
        F[i] = F[i-1] * 1ll * i % MOD;
        Finv[i] = Finv[i-1] * 1ll * inv[i] % MOD;
    }
}
int comb(int n, int m){//comb(n, m)就是C(n, m) 
    if(m < 0 || m > n) return 0;
    return F[n] * 1ll * Finv[n - m] % MOD * Finv[m] % MOD;
}
int main(){
    init();
}

组合数公式:
C(n,m) = n! * inv(m!)%mod *inv((n-m)!)%mod
inv()为逆元。

#define mod 1000000007
typedef long long LL;
const int maxn = 100000 + 10;
LL fac[maxn];//乘阶表
LL power(LL a,LL b){//快速幂
    a%=mod;
    LL ans = 1;
    while(b){
        if(b&1)ans = (ans*a)%mod;
        b>>=1;
        a = (a*a)%mod;
    }
    return ans;
}
LL inv(LL a){//返回逆元(费马小定理)
    return power(a,mod-2)%mod;
}
void solve(){//计算乘阶表
    fac[0] = 1;
    for(int i = 1;i<=maxn-1;i++){
        fac[i] = (fac[i-1]*i)%mod;
    }
}
LL comb(int n,int k){//返回组合数(组合数公式)
    if(k>n)return 0;
    return (fac[n]*inv(fac[k])%mod*inv(fac[n-k])%mod)%mod;
}
int main()
{
    solve();
    int n,k;
    while(scanf("%d%d",&n,&k)!=EOF){
        printf("%lld\n",comb(n,k));
    }
    return 0;
}

杨辉三角形求组合数

void YangHui() {  //杨辉三角求组合数
    memset(Triangle, 0, sizeof(Triangle));
    for (LL i = 1; i <= 100; ++i) {
        Triangle[i][0] = Triangle[i][i] = 1;
        for (LL j = 1; j < i; ++j){
            Triangle[i][j] = Triangle[i-1][j-1] + Triangle[i-1][j];
        }
    }
}

大组合数
当m和n都比较大,p反而比较小的时候,可以使用卢卡斯定理。

C(n, m) % p  =  C(n / p, m / p) * C(n%p, m%p) % p
typedef long long LL;
LL p[20],a[20];
int k;
LL Multiply(LL a,LL b,LL mod){//快速乘法取模
   LL res = 0;
   while(b){
      if(b&1)res = (res +a)%mod;
      b>>=1;
      a = (a + a)%mod;
   }
   return res;
}
LL ex_gcd(LL a,LL b,LL &x,LL &y){拓展欧几里得求逆元
    if(b==0){x = 1;y = 0;return a;}
    LL res = ex_gcd(b,a%b,x,y);
    LL temp = x;
    x = y;
    y = temp - a/b*y;
    return res;
}
LL inv(LL a,LL b){//求逆元
   LL x,y;
   LL ans = ex_gcd(a,b,x,y);
   if(ans!=1)return -1;
   if(x<0)x = (x%b+b)%b;
   return x;
}
LL comb(LL n,LL m,LL mod){//求组合数取模
    if(m>n||n<0||m<0)return 0;
    if(m==0||n==m)return 1;
    if(m>n-m)m = n-m;
    LL ca = 1,cb = 1;
    for(LL i = 0;i<m;i++){
        ca = ca*(n-i)%mod;//这里一定要每步取模否则超时
        cb=  cb*(m-i)%mod;
    }
    return ca*inv(cb,mod)%mod;
}
LL Lucas(LL n,LL m,LL mod){//卢卡斯定理递归版本
     if(m==0)return 1;
     return comb(n%mod,m%mod,mod)*Lucas(n/mod,m/mod,mod)%mod;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值