C(n,k) 的质因子分解 POJ - 2992

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;
}

看网上还有另一种比较聪明的解法。

其中用到了一个递推式,\large a[j][i]=j/p[i]+a[j/p[i]][i]

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;
}

猜你喜欢

转载自blog.csdn.net/qq_41703679/article/details/81435583