HDU3208(区间指数和)

题目:http://acm.hdu.edu.cn/showproblem.php?pid=3208

 

题意:给两个数a和b,然后在闭区间[a,b]内的每一个数y都可以表示成x^k=y,要求x尽量最小,k尽量最大,然后求所有的k之和。

 

分析:对于这个题,我们首先要知道是基于以下的事实来计算的:

对于一个数n,从1~n中假设有x个数是满足p^k形式的,这里的k最多到62,那么对于每一个k,我们需要找到这个数x满足x^k最

接近n,那么现在的问题就是对于每一个k找出对应的x,那么这个怎么找呢?

 

我们可以这样考虑,由于是找最接近的,我们可以先大致确定一个数r,那么可以通过r=pow(n,1/k)来计算,然后我们分别计

(r-1)^k,r^k,(r+1)^k,然后看这三个数哪个最接近n就行了,这里要注意(r+1)^k计算时可能会超过LL,所以有一些处理。

然后就是相当于容斥的部分了。

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <math.h>

using namespace std;
typedef long long LL;

const LL INF=1e18+300;
const LL T=(LL)1<<31;

LL num[105];

LL multi(LL a,LL b)
{
    LL ans=1;
    while(b)
    {
        if(b&1)
        {
            double judge=1.0*INF/ans;
            if(a>judge) return -1;
            ans*=a;
        }
        b>>=1;
        if(a>T&&b>0) return -1;
        a=a*a;
    }
    return ans;
}

LL find(LL x,LL k)
{
    LL r=(LL)pow(x,1.0/k);
    LL t,p;
    p=multi(r,k);
    if(p==x) return r;
    if(p>x||p==-1) r--;
    else
    {
        t=multi(r+1,k);
        if(t!=-1&&t<=x) r++;
    }
    return r;
}

LL Solve(LL n)
{
    int i,k=0;
    memset(num,0,sizeof(num));
    if(n<=3) return n;
    num[1]=n;
    for(i=2;i<63;i++)
    {
        num[i]=find(n,i)-1;
        if(!num[i]) break;
    }
    k=i;
    for(int i=k-1;i>0;i--)
        for(int j=1;j<i;j++)
            if(i%j==0) num[j]-=num[i];
    LL ans=num[1];
    for(int i=2;i<k;i++)
        ans+=(i*num[i]);
    return ans;
}

int main()
{
    LL n,m;
    while(cin>>m>>n)
    {
        if(m==0&&n==0) break;
        cout<<Solve(n)-Solve(m-1)<<endl;
    }
    return 0;
}


 

你可能感兴趣的:(HDU3208(区间指数和))