求组合数问题

对于组合数问题,有时我们需要根据题目所给的数据范围来决定是否要预处理出来,下面给出几道例子及其分析

 

给定 n 组询问,每组询问给定两个整数 ab请你输出 C(a,b)mod(10^9+7) 的值。

输入格式

第一行包含整数 n

接下来 n 行,每行包含一组 a 和 b

输出格式

共 n 行,每行输出一个询问的解。

数据范围

1n10000,
1ba2000

输入样例:

3
3 1
5 3
2 2

输出样例:

3
10
1

这道题数据范围不是很大,我们可以直接通过C(m,n)=C(m-1,n-1)+C(m-1,n)预处理出来
下面是代码
#include<bits/stdc++.h>
using namespace std;
const int N=2002;
const int mod=1e9+7;
int c[N][N];
void init()
{
    for(int i=1;i<N;i++)
    {
        c[i][0]=1;c[i][i]=1;
        for(int j=1;j<i;j++)
            c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
    }
}
int main()
{
    int n;
    cin>>n;
    init(); 
    int a,b;
    while(n--)
    {
        cin>>a>>b;
        printf("%d\n",c[a][b]%mod);
    }
    return 0;
}

 

 

给定 n组询问,每组询问给定两个整数 ab,请你输出 C(a,b)mod(109+7) 的值。

输入格式

第一行包含整数 n。

接下来 n 行,每行包含一组 a 和 b

输出格式

共 n 行,每行输出一个询问的解。

数据范围

1n10000,
1ba10^5

输入样例:

3
3 1
5 3
2 2

输出样例:

3
10
1

这道题的数据用上面的组合公式预处理显然不太可能,这个时候我们可以按照组合数定义预处理
C(a,b)=a!/b!/(a-b)!
有这个公式可得C(a,b)%p=(a!)*(b!)^-1*(a-b)!^-1%p,逆元可以通过费马定理定理求得
下面给出代码
#include<bits/stdc++.h>
using namespace std;
const int N=100003;
const long long mod=1e9+7;
long long fact[N],infact[N]; //fact[i]记录i的阶乘模p的值,infact[i]记录i的阶程模p的逆元
typedef long long LL;
LL qmi(LL a,LL b,LL p)//快速幂求逆元(只有互质情况下才能用)
{
    LL ans=1;
    while(b)
    {
        if(b&1)
            ans=ans*a%p;
        b>>=1;
        a=a*a%p;
    }
    return ans;
}
void init()
{
    fact[0]=infact[0]=1;
    for(int i=1;i<N;i++)
    {
        fact[i]=i*fact[i-1]%mod;
        infact[i]=qmi(fact[i],mod-2,mod)%mod;
    }
}
int main()
{
    init();
    int n;
    cin>>n;
    LL a,b;
    while(n--)
    {
        cin>>a>>b;
        printf("%lld\n",fact[a]*infact[a-b]%mod*infact[b]%mod);
    }
    return 0;
}

 

下面是一个数据范围更大的例子:

给定 n组询问,每组询问给定三个整数 a,b,p,其中 p 是质数,请你输出 C(a,b)modp 的值。

输入格式

第一行包含整数 n

接下来 n 行,每行包含一组 a,b,p

输出格式

共 n 行,每行输出一个询问的解。

数据范围

1n20,
1ba10^18,
1p10^5,

输入样例:

3
5 3 7
3 1 5
6 4 13

输出样例:

3
3
2
这道题运用的是lucas定理,分析在我前面的博客里面,有兴趣的小伙伴可以看一下!


#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL qmi(LL a,LL b,LL p)
{
    LL ans=1;
    while(b)
    {
        if(b&1)
            ans=ans*a%p;
        b>>=1;
        a=a*a%p;
    }
    return ans;
}
LL C(LL a,LL b,LL p)
{
    LL ans=1;
    for(int i=a,j=1;j<=b;i--,j++)
        ans=ans*i%p*qmi(j,p-2,p)%p;
    return ans;
}
LL lucas(LL a,LL b,LL p)
{
    if(a<p&&b<p) return C(a,b,p);
    return C(a%p,b%p,p)*lucas(a/p,b/p,p)%p;
}
int main()
{
    int n;
    cin>>n;
    LL a,b,p;
    while(n--)
    {
        cin>>a>>b>>p;
        printf("%lld\n",lucas(a,b,p));
    }
    return 0;
}


下面给出最后一道例子

输入 a,b,求 C(a,b) 的值。

注意结果可能很大,需要使用高精度计算。

输入格式

共一行,包含两个整数 a 和 b

输出格式

共一行,输出 C(a,b) 的值。

数据范围

1ba5000

输入样例:

5 3

输出样例:

10


这道题只让求一个值,显然出题人的意图不是让我们预处理出来所有组合数的,但是我们也不能直接按照组合数定义来求,那样还会涉及到高精度除法,比较麻烦
我们可以将组合数质因数分解,并将每一个质因数的幂次都记录下来,最后直接用高精度计算结果即可!(不会高精乘的小伙伴可以看下我之前的博客,里面介绍了一些高精度运算方法)
下面是代码


#include<iostream>
#include<vector>
using namespace std;
const int N=5002;
int prime[N];//素数表 
bool vis[N];
int fact[N];//记录质因素分解指数 
int cnt;
vector<int> ans;
void init()//预处理出来所有小于N的质数 
{
    for(int i=2;i<N;i++)
    {
        if(!vis[i]) prime[++cnt]=i;
        for(int j=1;j<=cnt&&i*prime[j]<N;j++)
        {
            vis[i*prime[j]]=true;
            if(i%prime[j]==0) break;
        }
    }
}
int get(int x,int t)//求x!中含有t的幂数和(值得记住方法) 
{
    int ans=0;
    while(x)
    {
        ans+=x/t;
        x/=t;
    } 
    return ans;
}
vector<int> mul(vector<int> ans,int k)//高精乘 
{
    int t=0;
    vector<int>c;
    for(int i=0;i<ans.size();i++)
    {
        t+=k*ans[i];
        c.push_back(t%10);
        t/=10;
    }
    while(t)
    {
        c.push_back(t%10);
        t/=10;
    }
    return c;
}
int main()
{
    init();
    int a,b;
    cin>>a>>b;    
    for(int i=1;prime[i]<=a;i++)//求出组合数的质因子分解式 
        fact[i]+=get(a,prime[i])-get(b,prime[i])-get(a-b,prime[i]);
    ans.push_back(1);
    for(int i=1;prime[i]<=a;i++)
        while(fact[i]--)
            ans=mul(ans,prime[i]);
    for(int i=ans.size()-1;i>=0;i--)
        printf("%d",ans[i]);
    return 0;
}

希望大家能够喜欢!

 
posted @ 2021-05-06 11:47  AC--Dream  阅读(97)  评论(0编辑  收藏  举报