POJ2992 Divisors(整数分解)

Divisors
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 10847 Accepted: 3214

Description

Your task in this problem is to determine the number of divisors of  Cnk. Just for fun -- or do you need any special reason for such a useful computation?

Input

The input consists of several instances. Each instance consists of a single line containing two integers n and k (0 ≤ k ≤ n ≤ 431), separated by a single space.

Output

For each instance, output a line containing exactly one integer -- the number of distinct divisors of  Cnk. For the input instances, this number does not exceed 2 63 - 1.

Sample Input

5 1
6 3
10 4


Sample Output

2
6
16

题目大意:

Cnk的素数因子的个数,由于n和k偏大,直接计算后算素数因子的个数显然不可能,所以我们可以利用一些数论技巧,我们需要知道几条定理:


(1) :n!的素因子分解中的素数p的幂为:

[n/p] + [n/p^2] + [n/p^3] ......+[n/p^n],(n >= p ^ n)

(2):若n的标准素因子分解表达式为n = p1 ^ a1 + p2 ^ a2 + p3 ^ a3 + ......pn^an,n的正因子的个数为ans = (a1 + 1) * (a2 + 1) * (a3 + 1) ......*(an + 1).


解题思路:

由于题目n和k给的范围是431,所以打表431以内的素数,然后依次从小到大枚举素数,计算幂值,当枚举完所有的素数幂值后,套用公式二得解。然后题目给的是Cnk,所以必须化成阶乘的形式。Cnk = n! / (n-k)!k!,计算的时候只要上面减掉下面两个就可以了。


AC代码:

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;

const int maxn = 1000;
bool isprime[maxn];
int prime[maxn];
int nprime;
int cnt[maxn];

void doprime() //筛法
{
	long long i,j;
	nprime = 0;
	memset(isprime,true,sizeof(isprime));
	isprime[1] = 0;
	for(i=2;i<=450;i++)
	{
		if(isprime[i])
		{
			prime[++nprime] = i;
			//cout<<nprime<<" "<<i<<endl;
			for(j = i*i;j<=450;j+=i)
			{
				isprime[j] = false;
			}
		}
	}
}
 
int calc(int num,int p) //递归计算素数的幂 
{
	if(num < p) //num / p = 0;
	{
		return 0;
	}
	if(p != 0)
		return num / p + calc(num/p,p);
}

long long com(int num1,int num2)
{
	long long ans = 1;
	int i;
	//cout<<nprime<<endl;
	for(i=0;i<=nprime;i++) //枚举质因数求幂 
	{
		cnt[i] = calc(num1,prime[i]); //计算因子的幂 
		cnt[i] -= calc(num1-num2,prime[i]);
		cnt[i] -= calc(num2,prime[i]);
		ans *= (cnt[i] + 1); //素数的幂+1连乘等于个数 
		//cout<<ans<<endl;
	}
	return ans;
}
int main()
{
	int m,n;
	doprime();
	while(scanf("%d%d",&m,&n) != EOF)
	{
		long long result = com(m,n);
		cout<<result<<endl;
	}
	return 0;
}	 



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值