求组合数三种算法

简介: 求组合数三种算法

递推法-杨辉三角

$q$组询问,每组询问两个整数,求$C_n^m\bmod (10^9+7)$
数据范围$1<=m<=n<=2000,求q<=10^4$
递推公式$C_n^m=C_{n-1}^m+C_{n-1}^{m-1}$
对于第一个数有选或者不选两个决策:

  • 选了,需要从剩下$n-1$个中选$m-1$个,$C_{n-1}^{m-1}$
  • 不选,需要从剩下$n-1$个中选$m$个,$C_{n-1}^m$

杨辉三角公式:

  • $C_n^0=C_n^m=1$
  • $C_n^m=C_n^{n-m}$

代码

const int N = 2010,mod=1e9+7;
int c[N][N];

void init(){
   
    for(int i=0;i<N;i++) c[i][0]=1;
    for(int i=1;i<N;i++)
        for(int j=1;j<=i;j++)
            c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
}

快速幂

$q$组询问,每组询问两个整数,求$C_n^m\bmod (10^9+7)$
数据范围$1<=m<=n<=10^5,q<=10^4$
$C_n^m=\frac{n!}{(n-m)!m!}$,用$f[x]存x!\bmod p$,用$g[x]存(x!)^{-1}\bmod p$,即乘法逆元
由于$p$是质数,n,m都是小于p的,所以n,m与p互质,由费马小定理:$a·a^{p-2}\equiv1(\bmod p)$,乘法逆元为$a^{p-2}$
查询:$C_n^m(\bmod p)=f[n]*g[n-m]*g[m](\bmod p)$

代码

const int N=1e5+10,mod=1e9+7;
int f[N],g[N];

LL qmi(LL a,LL b){
   
    LL res=1;
    while (b){
   
        if (b&1) res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}

void init(){
   
    f[0]=g[0]=1;
    for(int i=1;i<N;i++){
   
        f[i]=f[i-1]*i%mod;
        g[i]=g[i-1]* qmi(i,mod-2)%mod;
    }
}
LL get(LL n,LL m){
   
    return f[n]*g[m]%mod*g[n-m]%mod;
}

卢卡斯定理

求$C_n^m\bmod (p)$,p为质数
数据范围$1<=m<=n<=10^{18},p<=10^5$
卢卡斯定理:
$C_n^m=C^{\frac{m}{p}}_{\frac{n}{p}}C^{m\bmod p}_{n\bmod p} (\bmod p)$,其中p为质数

代码

#include <bits/stdc++.h>

using namespace std;

#define endl '\n'
#define debug(x) cout<<"a["<<x<<"]="<<a[x]<<endl;
#define pr(x) cout<<x<<endl;
#define IOS ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)

typedef long long LL;
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f;
const int N=1e5+10;
LL f[N],g[N];

LL qmi(LL a,LL b,int mod){
   
    LL res=1;
    while (b){
   
        if (b&1) res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}

void init(int mod){
   
    f[0]=g[0]=1;
    for(int i=1;i<N;i++){
   
        f[i]=f[i-1]*i%mod;
        g[i]=g[i-1]* qmi(i,mod-2,mod)%mod;
    }
}
LL get(LL n,LL m,int mod){
   
    return f[n]*g[m]%mod*g[n-m]%mod;
}

LL lucas(LL n,LL m,int mod){
   
    if (m==0) return 1;
    return lucas(n/mod,m/mod,mod)* get(n%mod,m%mod,mod)%mod;
}


int main() {
   
    IOS;
#ifndef ONLINE_JUDGE
    freopen("/Users/houyunfei/CLionProjects/MyCppWorkSpace/test.in", "r", stdin);
    freopen("/Users/houyunfei/CLionProjects/MyCppWorkSpace/test.out", "w", stdout);
#endif
    int n,m,mod;
    int t;
    cin>>t;
    while (t--){
   
        cin>>n>>m>>mod;
        init(mod);
        pr(lucas(n+m,m,mod))
    }

    return 0;
}
相关文章
|
20天前
82: 求组合数
82: 求组合数
|
2月前
|
机器学习/深度学习 算法 测试技术
【动态规划】C++算法:446等差数列划分 II - 子序列
【动态规划】C++算法:446等差数列划分 II - 子序列
|
2月前
|
算法 测试技术 C++
【动态规划】【数学】【C++算法】805 数组的均值分割
【动态规划】【数学】【C++算法】805 数组的均值分割
|
2月前
|
算法 测试技术 C++
【组合数学】【动态规划】【前缀和】1735生成乘积数组的方案数
【组合数学】【动态规划】【前缀和】1735生成乘积数组的方案数
|
8月前
|
算法
排列组合算法
排列组合算法
|
10月前
|
算法
Manachar算法(马拉车算法):快速求取最长回文子串
求取最长回文子串的长度的最佳方法为 Manachar算法 ,俗称马拉车算法。常见的方法就是中心扩散法,但时间复杂度较高。
70 0
Manachar算法(马拉车算法):快速求取最长回文子串
|
算法 C++
容斥原理算法的实现
容斥原理算法的实现
容斥原理算法的实现
|
算法 C++
状态压缩算法的实现
状态压缩算法的实现
状态压缩算法的实现
|
存储 算法
算法 |【实验5.3】:一元三次方程的根-连续区间的二分搜索求近似解
算法 |【实验5.3】:一元三次方程的根-连续区间的二分搜索求近似解
123 0
算法 |【实验5.3】:一元三次方程的根-连续区间的二分搜索求近似解