Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2901 Accepted Submission(s): 1416
Problem Description
Ignatius 喜欢收集蝴蝶标本和邮票,但是Eddy的爱好很特别,他对数字比较感兴趣,他曾经一度沉迷于素数,而现在他对于一些新的特殊数比较有兴趣。
这些特殊数是这样的:这些数都能表示成M^K,M和K是正整数且K>1。
正当他再度沉迷的时候,他发现不知道什么时候才能知道这样的数字的数量,因此他又求助于你这位聪明的程序员,请你帮他用程序解决这个问题。
为了简化,问题是这样的:给你一个正整数N,确定在1到N之间有多少个可以表示成M^K(K>1)的数。
Input
本题有多组测试数据,每组包含一个整数N,1<=N<=1000000000000000000(10^18).
Output
对于每组输入,请输出在在1到N之间形式如M^K的数的总数。
每组输出占一行。
Sample Input
10
36
1000000000000000000
Sample Output
4
9
1001003332
大概题意是要你输出1到n中,能够表示成a^b的数,a,b都是大于0的整数的个数,
其中b大于1。
因为1到n中,能够完全开平方的个数就是(n^0.5)的整数部分,以此类推可以得到,完全开立方,完全开四次方各种的次数。这样的话,要枚举的数量太大,有什么办法可以让枚举的数量减少呢?有的,由于任意一个大于1的整数都可以表示成两个素的乘积,于是,能够完全开平方的个数包括了能够完全开四次方,八次方,十六次方以此类推的个数。于是,可以知道,只需要枚举能够完全开素数次方的个数即可。又因为n最大不会超过10^18,由于64位整型号能够表示的最大数大概就是9*10^18多,所以不需要特地写个大数。又因为这样,所以素数只需要枚举到大小不超过63即可,因为2^63-1就是64位整型的最大值,所以这个n最大,开个63次方的整数部分结果肯定为1,为1的话就代表能够1到n中能够完全开这个次方的数只有1个,又因为1能够开任意次方,所以,这个数肯定是1啦,于是超过63的素数没必要枚举了,因为只有1能够开这么多次方。对于一个数n,从小到大枚举到使n开次方为1即可,再把前面所有开次方的结果都累加,再除去之中重复的部分,最终结果就是题意所要求的个数。
重复的部分是说,能够完全开六次方的肯定也能够完全开二次方和三次方,这个能完全开六次方的个数被重复加了一次,所以要减去一次,以此类推把所有重复的部分除去即可。
还有一点,这个题目,有的人用long long读入n的时候,会wa,这个的话,是各种编译器的原因,用cin读入就好了,至于有的人说缺失精度什么的,只是想多了。
#include
#include
using namespace std;
int prime[]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61};
void result(long long x)
{
int i,j,k;
long long tmp,ans=1;
for(i=0;;i++)
{
tmp=(long long)(pow(x,1.0/prime[i]));
if(tmp<2)
break;
ans+=tmp-1;
for(j=i+1;;j++)
{
tmp=(long long)(pow(x,1.0/(prime[i]*prime[j])));
if(tmp<2)
break;
ans-=tmp-1;
for(k=j+1;;k++)
{
tmp=(long long)(pow(x,1.0/(prime[i]*prime[j]*prime[k])));
if(tmp<2)
break;
ans+=tmp-1;
}
}
}
printf("%lld\n",ans);
}
int main()
{
long long x;
while(cin>>x)
result(x);
}