几种数据范围下求组合数

① 1<a<b<2000在这里插入图片描述

利用组合数的性质进行简单的递推预处理就可以快速求出组合数
需要注意的是当b为0时,C b a是1

#include<iostream>
using namespace std;
const int N=2020;
int c[N][N];
int mod=1e9+7;
void intial()
{
    for(int i=0;i<N;i++)
        for(int j=0;j<=i;j++)
            if(!j)  c[i][j]=1;
            else c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
}
int main()
{
    intial();
    int n;
    cin>>n;
    while(n--)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        cout<<c[a][b]<<endl;
    }
}

②1<a<b<1e5

在这里插入图片描述
将各个数的阶乘和逆元模上p的值先预处理出来,在直接利用组合数的计算公式求就行了。
为什么要进行取逆元的操作呢?
因为进行了除法运算在取模的情况下不等价,所以需要利用逆元求解。
注:某个数的逆元就是这个数的p-2次方

#include<iostream>
using namespace std;

const int N=100010,mod=1e9+7;
long long f[N],nif[N];
long long quick_power(int a,int b,int p)
{
    long long res=1;
    while(b)
    {
        if(b&1) res=(long long)res*a%p;
        a=(long long)a*a%p;
        b>>=1;
    }
    return res;
}
int main()
{
    f[0]=nif[0]=1;
    for(int i=1;i<N;i++)
    {
        f[i]=(long long)f[i-1]*i%mod;
        nif[i]=(long long)nif[i-1]*quick_power(i,mod-2,mod)%mod;
        
    }
    int n;
    cin>>n;
    while(n--)
    {
        int a,b;
        cin>>a>>b;
        cout<<(long long)f[a]*nif[a-b]%mod*nif[b]%mod<<endl;
    }
}

③1<b<a<1e18

在这里插入图片描述

当数据范围有第三种情况这么大的时候,只能利用卢卡斯定理了。
能处理的数据范围变大了,不过运行速度因为需要递归处理所以也变慢了,所以数据范围没有特别大的情况下,用上面两种方法其实更好

#include<iostream>
using namespace std;
typedef long long LL;

long long quick_power(long long a,long long b,int p)
{
    long long res=1;
    while(b)
    {
        if(b&1) res=(long long )res*a%p;
        a=(long long)a*a%p;
        b>>=1;
    }
    return res;
}
long long C(long long  a,long long b,int p)
{
    if (b > a) return 0;
    long long res=1;
    for(int i=1,j=a;i<=b;i++,j--)
    {
       res=(long long )res*j%p;
       res=(long long )res*quick_power(i,p-2,p)%p;
    }
    return res;
}
long long lucas(long long a,long long b,int p)
{
    if(a<p&&b<p)    return C(a,b,p);
    return (long long)lucas(a/p,b/p,p)*C(a%p,b%p,p)%p;//因为a和b很大,所以即使/p还是有可能不能直接计算,所以需要递归调用lucas这个函数
    
}
int main()
{
    int n;
    cin>>n;
    
    while(n--)
    {
        long long a,b;int p;
        cin>>a>>b>>p;
        cout<<lucas(a,b,p)<<endl;
    }
    return 0;
}

参考文献 acwing 算法基础课

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值