求组合数(不同类型的组合数C++)

求组合数有许多种不同的算法,要根据不同的数据量大小选择不同的算法

类型1

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

输入格式

第一行包含整数 n。

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

输出格式

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

数据范围

1≤n≤10000,
1≤b≤a≤2000

输入样例:

3
3 1
5 3
2 2

输出样例:

3
10
1

类型1思路

此时的参数为

 1≤n≤10000,
1≤b≤a≤2000

这个例子的n很大,a,b很小,所以我们可以预处理出所有的c(a b)组合数,利用DP的思想

c(a b)可以看作是从a个苹果中选取b个苹果,

若首先选择选择一个苹果,则组合数可拆分为两种情况

包含该苹果或不包含该苹果,刚好涵盖了所有情况 

 类型1代码

#include<iostream>

using namespace std;

int mod = 1e9 + 7;
const int N = 2010;
int f[N][N];
int n;

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

int main()
{
	init();
	
	cin >> n;
	for(int i = 0; i < n; i ++)
	{
		int a, b;
		cin >> a >> b;
		cout << f[a][b] << endl;
	}
	
	return 0;
} 

 类型2

数据范围类型:

1≤n≤10000,
1≤b≤a≤105

类型2思路

 a,b数据也较大,先预处理出各个数的阶乘,然后再模拟手工计算,利用公式算出结果。

由于结果中有除法,且为大数,可能会有溢出,因此这里用了逆元,逆元可以用快速幂来算。

类型2代码

#include<iostream>

using namespace std;

typedef long long LL;
int p = 1e9 + 7;
const int N = 1e5 + 10;
int fact[N], infact[N];//阶乘和逆
 
int qmi(int a, int k, int p)
{
	int res = 1;
	while(k)
	{
		if(k & 1) res = (LL) res * a % p;
		a = (LL) a * a % p;
		k >>= 1;
	}
	
	return res;
}

int main()
{
	int n;
	cin >> n;
	
	fact[0] = infact[0] = 1;
	for(int i = 1; i < N; i ++)
	{
		fact[i] = (LL) fact[i - 1] * i % p;
		infact[i] = (LL) infact[i - 1] * qmi(i, p - 2, p) % p;
	}
	
	while(n --)
	{
		int a, b;
		cin >> a >> b;	
		int res;
		res = (LL) fact[a] * infact[b] % p * infact[a - b] % p;
		cout << res << endl;
	}
	
	return 0;
}

类型3

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

输入格式

第一行包含整数 n。

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

输出格式

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

数据范围

1≤n≤20,
1≤b≤a≤1018,
1≤p≤105,

输入样例:

3
5 3 7
3 1 5
6 4 13

输出样例:

3
3
2

类型3思路

 这个数据范围,a,b都是暴大的,所以利用lucass定理进行拆分

类型3代码

#include<iostream>

using namespace std;

typedef long long LL;

int p;

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

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

LL lucass(LL a, LL b)
{
	if(a < p && b < p) return C(a, b);
	else return C(a % p, b % p) * lucass(a / p, b / p) % p;
}

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

类型4

输入 a,b,求 Cab 的值。

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

输入格式

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

输出格式

共一行,输出 Cab 的值。

数据范围

1≤b≤a≤5000

输入样例:

5 3

输出样例:

10

类型4思路

这次ab数字都很大,需要用到高精度乘法,为了方便计算,采用一下计算步骤

1.分解质因数(Cab),每个质数p的次数等于a中p的次数减去b中p的次数减去(a - b)中p的次数(因为是除法),利用get函数求出次数

2.高精度乘法算出

类型4代码

#include<iostream>
#include<vector>

using namespace std;

const int N = 5010;
bool st[N];
int primes[N];//质数
int sum[N];//质数的次数
int cnt;

void getprimes(int a)
{
	for(int i = 2; i <= a; i ++)
	{
		if(!st[i]) primes[cnt ++] = i;
		for(int j = 0; primes[j] <= a / i; j ++)
		{
			st[primes[j] * i] = true;
			if(i % primes[j] == 0) break;
		}	
	}	
} 

int get(int a, int p)
{
	int res = 0;
	while(a)
	{
		res += a / p;
		a /= p;
	}
	return res;
}

vector<int> mul(vector<int> a, int b)
{
	vector<int> c;
	int t = 0;
	for(int i = 0; i < a.size(); i ++)
	{
		t += a[i] * b;
		c.push_back(t % 10);
		t /= 10;
	}
	
	while(t)
	{
		c.push_back(t % 10);
		t /= 10;
	}
	return c;
}

int main()
{
	int a, b;
	cin >> a >> b;
	
	getprimes(a);
	
	for(int i = 0; i < cnt; i ++)//分解质因数 
		sum[i] = get(a, primes[i]) - get(b, primes[i]) - get(a - b, primes[i]);
		
	
	vector<int> res;
	res.push_back(1);
	
	for(int i = 0; i < cnt; i ++)
		for(int j = 0; j < sum[i]; j ++)
			res = mul(res, primes[i]);
	
	for(int i = res.size() - 1; i >= 0; i --)
		cout << res[i]; 
		
		
	
	return 0;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值