hdu-2204-Eddy's爱好(容斥原理)

Eddy's爱好

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

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 我的思路是这样的:
  把n作为double型数,对其开k次方(我们不难发现,
k<61, 对于题目要求的n的最大值开61次方就小于2了)得m,则不大 m的正整数的k次方就都是要求的整数。如果我们把k从2到60循环并把(int)m都加起来,这个和是不是就是要求的答案呢? 很明显不是,如2的4次方和4的平方都是16、2的6次方和4的平方都等于64,这样以来这个和就可能大于要求的答案了。所 以k不能这样循环。我们这样:既然2的4次方和4的平方都是16、2的6次方和4的平方都等于64,那么我们何不只计4的2次方 和4的3次方不计2的4次方和2的6次方呢!即我们只需计一个数的素数次方,这样我们只用把60以内素数计下来进行计算就会 去掉了不少的重复。
  是不是这样算出的结果t就是答案呢?还不是。这一点就不太好想了,让我费好长时间也找不出反例,还是同学提示才算 找到了。比如64(2的2*3次方)既是4的3次方又是8的平方。这一类属于m的k*p次方型(其中k和p为素数),这样 以来t的值还比答案大,我们就得去掉这一部分。我们将60以内的素数两两相乘取60以内的积的集合,再对n开方并去掉这部 分的重复,这样得到t的就去掉了m的k*p次方型的数重复计算。
  看起来没有什么问题了,可是用上面的测试实例一测还是不对。这又是哪里出错了。经过以上种种出错,这次就不难想 出原因了。对于m的k*p*g型的数(k、p、g都是素数),它既是(m^k)^(p*g),又是(m^p)^(k*g),同时又是(m^g)^(k*p) ,这样我们的上一部操作又多去掉了一些值。这里我们把其加上就是了。对于这一类型的数在10^18之内只可能有三个: 2的2*3*5次方、2的2*3*7次方以及3的2*3*5次方。如果n包括这几个数,再加上包括的个数算出的t就是最终答案了。
  以下是代码及其简要解析:
//以下的k、p、g都指的素数
#include<stdio.h>
#include<math.h>
int main()
{
    int i,t,m;
    //数组a存60以内的素数,数组b存60以内的k*p型数据
    double n,a[20]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59};
    double k,b[18]={6,10,14,15,21,22,26,33,34,35,38,39,46,51,55,57,58};
    while(scanf("%lf",&n)!=-1){
        t=0;
        //计算m^k型数的个数
        for(i=0;i<17;i++){
            m=(int)pow(n,1/a[i]);
            //由于大数开方会有数据的损失,这里的while循环是处理数据损失的
            while(pow(m+1,a[i])<=n)m++;
            if(m<2)break;
            t+=m-1;
        }
        //以上没有计算1,这里要把1 算上
        t++;
        //去掉m^(k*p)型数的个数
        for(i=0;i<17;i++){
            m=(int)pow(n,1/b[i]);
            while(pow(m+1,b[i])<=n)m++;
            if(m<2)break;
            t-=m-1;
        }
        //补充m^(k*p*g)型数(10^18内就这三个)数据的个数
        if(n>=pow(2,30))t++;
        if(n>=pow(2,42))t++;
        if(n>=pow(3,30))t++;
        printf("%d\n",t);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值