求组合数(四类)

传送

求组合数 I

在这里插入图片描述
在这里插入图片描述
思路: 很明显,a和b 比较小,直接递推即可。
公式:C[i][j] = C[i-1][j] +C[i-1][j-1] 。 提供两种理解角度: 1、杨辉三角形(可自行百度)。2、组合公式理解, 从 i 里选 j 个的方案数由两部分,设k是 i 中某一个, 则选取 k 和不选取 k :C[i-1][j] + C[i-1][j-1]

#include<bits/stdc++.h>

using namespace std;
const int MAX = 2010, mod = 1e9+7;

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

int main()
{
    init();
    int T;
    cin>>T;
    while(T--){
        int a,b;
        cin>>a>>b;
        cout<<c[a][b]<<endl;
    }
    return 0;
}

求组合数 II

在这里插入图片描述
在这里插入图片描述

思路: a和b 较大,采用逆元模式,由于n较大同时预处理。
公式:C(a , b) = a! * infact( b! ) * infact( (a-b)! )

#include<bits/stdc++.h>
using namespace std;
const int mod = 1e9+7,MAX = 1e5+7;
typedef long long LL;

int fact[MAX];  // 阶乘;
int infact[MAX]; // 阶乘的逆元;

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

void init()
{
    fact[0] = 1, infact[0] = 1;
    for(int i=1;i<MAX;i++){
        fact[i] = (LL)fact[i-1]* i %mod;
        infact[i] = (LL)infact[i-1]* quick(i,mod-2)%mod; //这里 infact(ab)=infact(a)*infact(b);
    }
}

int main()
{
    init();
    int T;
    scanf("%d",&T);
    while(T--){
        int a,b;
        scanf("%d%d",&a,&b);
        printf("%d\n",(LL) fact[a] * infact[b]%mod* infact[a-b]%mod );    //注意加 LL
    }
    return 0;
}

求组合数 III

在这里插入图片描述
在这里插入图片描述
思路: 由于a,b非常大而 p 却不大,很明显在考察Lucas定理:Lucas定理是用来求 c(n,m) mod p的值的,p为质数。
公式:
来自百度百科

#include<bits/stdc++.h>

using namespace std;
typedef long long LL;

LL a,b,p;

int quick(int a,int b){
	int ans =1;
	while(b){
		if(b&1) ans = (LL) ans*a % p;
		a = (LL)a*a % p;
		b>>=1;
	}
	return ans;
}

int C(int a,int b){
	int ans = 1;
	for(int i= 1,j= a; i<=b ;i++,j--){
		ans = (LL)ans *j % p;
		ans = (LL)ans * quick(i,p-2)% p;
	}
	return ans;
}

int lucas(LL a,LL b){
	if(a<p && b<p) return C(a,b) ;
	return (LL)lucas(a/p,b/p) * C(a%p,b%p) % p;  //递归调用 直到 t=0 
}

int main()
{
	int n;
	cin>>n;
	while(n--){
		cin>>a>>b>>p;
		cout<<lucas(a,b)<<endl;
	}
	return 0;
 } 

求组合数 IV

在这里插入图片描述
在这里插入图片描述
思路: 题目没有取模,且提示使用高精度,如果我们直接写的话,需要使用大数乘法和大数除法 两种大数运算,而且效率比较低,所以这里我们采用质因数分解法解决此问题( 详情请移步 阶乘分解)

#include<bits/stdc++.h>

using namespace std;
const int MAX = 5010;

int primes[MAX] ,cnt;
bool st[MAX];

void init(int n)
{
	for(int i=2;i<=n;i++){       //线性筛 (埃氏筛优化)
		if(!st[i]) primes[cnt++] = i;
		for(int j=0;primes[j]*i<=n;j++){
			st[primes[j]*i] = 1;
			if (i%primes[j]==0) break;
		}
	}
}

int get(int n,int p)
{
	int ans=0;   // 求 p 因子在n的阶乘中出现的次数(P ^ ans);
	while(n){
		ans+=n/p;
		n/=p;
	}
	return ans;
}

vector<int> mul(vector<int> a,int x)	//大数乘法
{
	vector<int> ans;
	int t = 0;
	for(int i=0;i<a.size() || t ;i++){
		if(i<a.size()) t += a[i]*x ;
		ans.push_back(t%10);
		t/=10;
	}
	return ans;
}

int main()
{
	int a,b;
	cin>>a>>b;
	init(a);
	
	vector<int> ans;
	ans.push_back(1);
	for(int i=0;i<cnt;i++){
		int p = primes[i];
		int num = get(a,p) - get(b,p) - get(a-b,p);   // 由 a! / b!/ ( (a-b)! )求此质因子的次幂
		for(int j=0;j<num;j++){
			ans = mul(ans,p);
		}
	}
	for(int i=ans.size()-1;i>=0;i--) cout<<ans[i];
	cout<<endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值