求组合数IV

思路:

(1)对于非取模类型,卢卡斯定理,费马小定理失效,应直接计算。

(2)考虑将C(a,b) = a!/(b!*(a - b)!) 进行质因子分解。

(3)由于数据较小,可以逐个质数讨论,计算该质数a!有多少个,b!有多少个,(a - b)!有多少个,将之减,则为C(a,b)中有多少该质因子,存起来,计算完毕后,再用高精度乘法将各个质因子相乘即可。

(4)流程:

  1. 线性筛质数,小于等于a的。
  2. 对于每个质数,维护sum[i]描述第i个质数在C(a,b)中有多少个,其中sum[i] = get(a) - get(b) - get(a - b);
  3. 逐个质数讨论,对于每个质数,高精度乘sum[i]次。
  4. 输出结果。
  5. 对于get(x)用于求x!中能拆解多少个质数p,由于0~p之间的阶乘有0个p,p~2p有1个p所以对于a!,a/p能算出单p数有多少个,同理a/p^2算出双p数的个数...将之相加即为a!拆解后p的个数,
  6. 对于高精度乘法,由于输入是倒置的,所以从0~res.size() - 1,逐位乘,用t做辅助算子,每次t%10入列,t/=10;最后对t进行收尾。

代码:

#include 

using namespace std;

const int N = 5010;
int prime[N],st[N],sum[N];
int cnt;
void get_prime(int n)
{
	for(int i = 2;i <= n;i ++)
	{
		if(!st[i]) prime[cnt ++] = i;
		
		for(int j = 0;i <= n/prime[j];j ++)
		{
			st[i*prime[j]] = 1;
			if(i % prime[j] == 0) break;
		}
	}
}

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

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

int main()
{
	int a,b;
	cin >> a >> b;
	
	get_prime(a);
	
	for(int i = 0;i < cnt;i ++)
		sum[i] = get(a,prime[i]) - get(b,prime[i]) - get(a - b,prime[i]);
	
	vector res;
	res.push_back(1);
	
	for(int i = 0;i < cnt;i ++)
		for(int j = 0;j < sum[i];j ++)
			res = mul(res,prime[i]);
	
	for(int i = res.size() - 1;i >= 0;i --) cout << res[i] ;
	
	return 0;
		
}

你可能感兴趣的:(算法)