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
比赛的时候一直TLE,赛后发现数据太多需要打表……
打了个表很轻松就过了。
#include <cstdio>
#include <iostream>
#include <string.h>
#include <algorithm>
using namespace std;
bool g[500];
int m[500], y[500], a[500][500]; //a[i][j]表示i!的j的幂次
int num;
long long ans;
int t,k;
void shai() //筛质数
{
for (int i=0; i<500; i++) g[i]=0;
g[1]=1;
num=0;
for (int i=2; i<500; i++)
if (!g[i])
{
for (int j=2; i*j<500; j++)
g[i*j]=1;
y[++num]=i;
}
}
void pre_solve()
{
memset(a, 0, sizeof(a));
for (int s=1; s<435; s++)
{
int x=s;
memcpy(a[s], a[s-1], sizeof(a[s]));
if (!g[x])
{
a[s][x]++;
continue;
}
for (int i=1; y[i]<=x,x>1,i<=num; i++)
{
while (x % y[i] == 0)
{
a[s][y[i]]++;
x/=y[i];
}
}
}
}
void solve()
{
ans=1;
for (int i = 2; i < 435; ++i)
{
ans*=(a[t][i]-a[t-k][i]-a[k][i]+1);
}
}
int main()
{
shai();
pre_solve();
while (scanf("%d%d", &t, &k)!=EOF)
{
solve();
printf("%lld\n", ans);
}
return 0;
}
看网上还有另一种比较聪明的解法。
其中用到了一个递推式,
a[j][i]
表示 j!的第i个素数的幂次。
这个递推式就是说在j个数里把p[i]个数放一堆,每堆里的最后一个数取出来,其他的数都不是p[i]的倍数且与p[i]互质;然后就有了j/p[i]个幂次。再把每一个数除以p[i],得到的就是从1到j/p[i]的数组,剩余的没加上的幂次就是a[j/p[i]][i]了。
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
bool Prime[500];
int jie[500][90];
__int64 zu[500][500];
int Primer[500],num;
void GetPrime()
{
for(int i = 2; i <= 431; ++i)
Prime[i] = true;
for(int i = 2; i <= 431; ++i)
{
if(Prime[i])
{
for(int j = i+i; j <= 431; j+=i)
Prime[j] = false;
}
}
num = 0;
for(int i = 0; i <= 431; ++i)
if(Prime[i])
Primer[num++] = i;
}
void solve()
{
//阶乘分解为 素因子相乘的形式jie[j][i]表示 j! 第i个素数的幂为多少
for(int i = 0;i < num; ++i)
for(int j = 2; j <= 431; ++j)
jie[j][i] = j/Primer[i] + jie[j/Primer[i]][i];
for(int i = 2; i <= 431; ++i) //计算因子个数
{
for(int j = 1; j < i; ++j)
{
zu[i][j] = 1;
for(int k = 0; k < num && jie[i][k]; ++k)
{
int side = jie[i][k] - jie[j][k] - jie[i-j][k]; //计算C(i,j)中第i个素数的幂为多少
if(side)
zu[i][j] *= (side+1); //求因子个数
}
}
}
}
int main()
{
GetPrime();
solve();
int n,m;
while(~scanf("%d%d",&n,&m))
{
if(m==0 || m==n)
printf("1\n");
else
printf("%I64d\n",zu[n][m]);
}
return 0;
}
第三种解法,原理与第二种类似,只是在求n!对质数prime[i]的指数时的写法不同,第二种解法用的是动态规划思想,而这一种是while语句的递归(or 递推?傻傻分不清楚)。
简单解释下:1~n中prime[i]的倍数的个数加到幂次的计数器中,因为这些倍数中还有幂次比1更高的,所以再把prime[i]的二次方的倍数的个数加到计数器中;还有三次方,四次方……更高次方的倍数的个数,一直加到没有了为止。
#include <iostream>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
typedef long long ll;
#define INF 0xfffffff
#define MAX(a,b) a>b?a:b
#define MIN(a,b) a>b?b:a
#define M 1000
short int flag[M];
int prime[500];//当M比较大的时候(大于10e5)素数大概就是范围的1/10
int q=0;//素数的下标
void sieve_prime() //筛法求素数
{
memset(flag,0,sizeof(flag));
int i;
for(i=2;i*i<M;i++) { //边筛边存
if(flag[i]) continue;
prime[q++]=i;
for(int j=i*i;j<M;j+=i)
flag[j]=1;
}
for(i;i<M;i++) //存另一部分素数
if(!flag[i]) prime[q++]=i;
}
int a[M],b[M];
int resolve_factorial_0(int n)
{
int ans,temp;
for(int i=0;n>=prime[i];i++)
{
temp=prime[i];
ans=0;
while(temp<=n) ans+=n/temp,temp*=prime[i];
a[prime[i]]+=ans;
}
}
int resolve_factorial_1(int n)
{
int ans,temp;
for(int i=0;n>=prime[i];i++)
{
temp=prime[i];
ans=0;
while(temp<=n) ans+=n/temp,temp*=prime[i];
b[prime[i]]+=ans;
}
}
void solve(int n)
{
ll ans=1;
for(int i=0;prime[i]<=n&&a[prime[i]];i++)
ans*=(a[prime[i]]-b[prime[i]]+1);
printf("%I64d\n",ans); //最好用printf。
//cout<<ans<<endl;
}
int main()
{
int i,j,k,t;
int m,n;
ll ans;
sieve_prime();
while(scanf("%d%d",&n,&k)!=-1) //最好用scanf,
{
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
resolve_factorial_0(n);//将n!进行分解。
resolve_factorial_1(k);
resolve_factorial_1(n-k);
solve(n);
}
return 0;
}