Poj 2992 Divisors(算数基本定理&素数因子个数)

我们首先来看一下题面:

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:在这个问题中,你的任务是确定C_{n}^{k}的因子个数。说着玩的——或者你需要我对这种益处多多的计算给出任何特别的理由吗?

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:对于每个测试实例,输出仅包含一个整数的一行——C_{n}^{k}的不同因子个数。对于每个测试实例,这个数字不会超过long long int的范围。

Sample Input

5 1
6 3
10 4

Sample Output

2
6
16

  我们看到这个题的第一反应是用算数基本定理(唯一分解定理)把C_{n}^{k}的因子一个一个撬出来,但是仔细一想这在C++的普通数据范围内绝不可能实现——C_{431}^{230}=8.041312309262014e+127,甩了long long int和unsigned long long int几十条街(1e18和2e18),所以我们必须另寻他途。

  这个时候我们想到了一个关于一个数N里质数因子P的指数幂的大小的公式,求出幂之后用算数基本定理求解:

power(P)=\frac{N}{P}+\frac{N}{P^{2}}+...+\frac{N}{P^{n}}

 怎么理解呢?首先我们举一个例子:

10!=1\times 2\times3\times4\times5\times6\times7\times8\times9\times10

我们想求2这个质数因子在10!里面幂的大小,首先我们计算\frac{10}{2},这个意思是10以内有5个2的倍数,把这一部分刨去之后(假设他是一层一层的刨,即对一个数就除一次2),上式就会变成这样:

10!=1\times1\times3\times2\times5\times3\times7\times4\times9\times5'

可以看到,本次处理从2,4,6,8,10中分别析出一个2。

可以看到这个式子里还是可以分解出很多2,所以我们接下来计算\frac{10}{2^{2}}(整除)得到2,刨去这一层,式子会变成这样:

10!=1\times1\times3\times1\times5\times3\times7\times2\times9\times5

可以看到,本次处理从2(原来的4),4(原来的8)里再次析出了两个2。

仍然没有分解干净,还需要继续处理:\frac{10}{2^{3}}得到1,原式变为:

10!=1\times1\times3\times1\times5\times3\times7\times1\times9\times5

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

猜你喜欢

转载自blog.csdn.net/weixin_43874261/article/details/88372999