我们首先来看一下题面:
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?
translation:在这个问题中,你的任务是确定的因子个数。说着玩的——或者你需要我对这种益处多多的计算给出任何特别的理由吗?
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.
translation:输入包含多个实例(多组输入警告)。每个实例包含一行,包含两个正数n和k,用空格分隔。
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.
translation:对于每个测试实例,输出仅包含一个整数的一行——的不同因子个数。对于每个测试实例,这个数字不会超过long long int的范围。
Sample Input
5 1
6 3
10 4
Sample Output
2
6
16
我们看到这个题的第一反应是用算数基本定理(唯一分解定理)把的因子一个一个撬出来,但是仔细一想这在C++的普通数据范围内绝不可能实现——=8.041312309262014e+127,甩了long long int和unsigned long long int几十条街(1e18和2e18),所以我们必须另寻他途。
这个时候我们想到了一个关于一个数N里质数因子P的指数幂的大小的公式,求出幂之后用算数基本定理求解:
怎么理解呢?首先我们举一个例子:
我们想求2这个质数因子在10!里面幂的大小,首先我们计算,这个意思是10以内有5个2的倍数,把这一部分刨去之后(假设他是一层一层的刨,即对一个数就除一次2),上式就会变成这样:
'
可以看到,本次处理从2,4,6,8,10中分别析出一个2。
可以看到这个式子里还是可以分解出很多2,所以我们接下来计算(整除)得到2,刨去这一层,式子会变成这样:
可以看到,本次处理从2(原来的4),4(原来的8)里再次析出了两个2。
仍然没有分解干净,还需要继续处理:得到1,原式变为:
从2(原来的8)里拆出一个2,现在再也没有2这个质数因子了。
因为阶乘都是乘法,所以每一层的处理都要计算总和,这里的power(2)=5+2+1=8,根据算术基本定理,可以得出10!有9个不同因子。
说白了,这个公式就是一层一层的剥洋葱,首先把因子的一次方的倍数(二次方及以上都是)全部剥一遍,再把二次方的倍数剥一遍,直到拆干净为止。
下面是实现代码:
#include <iostream>
#include <cstdio>
//#include <bits/stdc++.h>
#include <iomanip>
#include <cstring>
#include <cmath>
#define DETERMINATION main
#define lldin(a) scanf("%lld",&a)
#define din(a) scanf("%d",&a)
#define reset(a,b) memset(a,b,sizeof(a))
const int INF=0x3f3f3f3f;
using namespace std;
typedef long long ll;
typedef long double ld;
/*Although there will be many obstructs ahead of you,
the desire for victory still fills you with determination..*/
ll collocation[500][500],factors[500][500];
ll primes[500],cnt=0;
bool vis[500];
void euler()//因为算数基本定理和质数因子幂公式仅对质数有效,所以我们需要筛选出这些质数。
{
for(int i=2;i<=431;i++)
{
if(vis[i]==0)
{
primes[cnt++]=i;
}
for(int j=0;j<cnt&&i*primes[j]<=431;j++)
{
vis[i*primes[j]]=1;
if(i%primes[j]==0)
break;
}
}
}
void solution()
{
for(int n=2;n<=431;n++)//n的阶乘
for(int i=0;i<cnt;i++)//筛选出的质数
{
factors[n][i]=n/primes[i]+factors[n/primes[i]][i];
//质数因子幂公式的递推版本,即n!中primes[i]的幂数。
}
for(int n=2;n<=431;n++)//组合数里的n
for(int m=1;m<n;m++)//组合数里的m
{
collocation[n][m]=1;//初始化一下当前C(N,M)的因子个数
for(int k=0;k<cnt;k++)
{
int ans;
ans=factors[n][k]-factors[m][k]-factors[n-m][k];
//显然,根据组合数基本公式,如果我们要从一个阶乘里除去另一个阶乘,那前阶乘的因子要减去
//后因子的数量,因为后者的部分已经被除去了。
if(ans)
collocation[n][m]*=(ans+1);//算数基本定理
}
}
}
int DETERMINATION()
{
euler();
solution();
int n,k;
while(cin>>n>>k)
{
if(n==k||k==0)
cout<<1<<endl;
else
cout<<collocation[n][k]<<endl;
}
return 0;
}