能量球
(enb.pas/c/cpp)
题目描述
sideman 有着一个你们所不知道的秘密。实际上,sideman不是地球人,而是遥远的Gliese 行星的生物。sideman 虽然身在地球,但是时刻盼望着能够回到Gliese 去。终于有一天,sideman 得到了几颗蕴含着无数能量的能量球。经过漫长艰苦的研究,sideman 搞清楚了这些能量球的规律。首先,能量球上面有一个正整数N,运用激发手段可以使得能量球上的正整数变为原来的数的一个真因子(A 是B 的一个真因子,等价于B 是A 的整数倍且0<A<B),并且放出单位数量的能量。当然,变为哪一个真因子是可以自由选择的。现在sideman 想知道每颗能量球最多可以放出多少能量,以及有多少种方式达到这个目的(两种操作方式被认为是不同的,当且仅当它们的操作序列中存在不同的真因子)。为了
帮助sideman 返回家乡,这个问题就交给你了。
输入格式
输入文件包含多组测试数据。
每组测试数据包含一个正整数 N,表示要计算的能量球。
输入文件以 EOF 结束。
输出格式
对于每组测试数据,在单独的一行内输出两个正整数,分别表示最多能够放出的能量值
和方案数。
样例输入
2
6
样例输出
1 1
2 2
数据规模及约定
对于30% 的数据,保证N≤1000。
对于 100% 的数据,保证 N≤109,测试数据不多于100 组。
思路
能量球这个题目需要一个组合数公式:
所以知道这个公式是有必要的,这个题运用公式的不同之处就是在于需要算出总的材料(因数)数量ans,然后求它的阶乘,分别将各种因子的数量减一,然后求它们各自的阶乘,把他们作为除数,ans作为被除数进行运算,数字虽然超过了int的范围,但是没有超出long long,所以可以直接运算。
注意求真因子的一个技巧(当然也是必须掌握的)就是循环只需要让N的因子x变化区间控制在x∈[2,√N+1] (x∈N*)(√N+1也可以直接用cmath库里面的ceil函数向上取整),如果从变化区间为x∈[2,N/2] (x∈N*),那么时间复杂度会大幅增加,只拿10^8做例子,同样是查因子,10^4显然比5×10^7更快,而且效果一样。
求完了因子总数量,就需要求ans个数,具体过程第一段已详细说明,不再赘述。
代码
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
int i,j,m,n,t,xx;
long long sum,temp,ans;
long long a[1000001];
long long mul(long long xx)
{
long long aans=1;
for(long long i=2;i<=xx;i++)
{
aans*=i;
}
return aans;
}
int main()
{
freopen("enb.in","r",stdin);
freopen("enb.out","w",stdout);
while(scanf("%d",&xx)==1)
{
memset(a,0,sizeof(a));
temp=xx;
for(i=2;i<=ceil(sqrt(xx));i++)
{
while(temp%i==0)
temp/=i,sum++,a[i]++;
}
if(temp>1)sum++;
ans=mul(sum);
for(i=2;i<=ceil(sqrt(xx));i++)
if(a[i]>1)
ans/=mul(a[i]);
cout<<sum<<" "<<ans<<endl;
sum=0,ans=0;
}
}