容斥原理——hdu3208

和hdu2204有点像

这题要特别注意精度问题,如pow的精度需要自己搞一下,然后最大的longlong可以设为1<<31

/*
只要求[1,n]范围内的sum即可
那么先枚举幂次k[1,63]
然后求 x^k<=n,x的最大取值sum[i](这里要扩大一下pow的精度) 
然后进行容斥,j%i==0,si-=sj 
*/
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define INF (ll)1<<31
#define esp 1e-18 
ll sum[100];
const double inf=1e18+500;
/*
ll Pow(ll a,ll b){//防越界的快速幂
    ll res=1;
    while(b){
        if(b%2){
            double judge=1.0*inf/res;
            if(a>judge)return -1;//必须先判断res*a是否会越界 
            res=res*a;
        }
        b>>=1;
        if(a>INF && b>0)return -1;//判断a是否越界了 
        a=a*a; 
    } 
    return res;
} 

ll calc(ll k,ll n){
    ll x=(ll)pow(n,1.0/k),t,p;
    p=Pow(x,k);
    if(p==n)return x;//刚好相等 
    if(p>n || p==-1) x--;//越界了 
    else {//判断x是否能再+1 
        t=Pow(x+1,k);
        if(t!=-1 && t<=n)x++; 
    }
    return x;
} */
 
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>INF&&b>0) return -1;
        a=a*a;
    }
    return ans;
}
ll Find(ll x,ll k)//手动扩大pow精度
{
    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){
    if(n==0)return 0;
    ll res=0,Max=0;
    memset(sum,0,sizeof sum);
    sum[1]=n;
    for(ll k=2;k<=63;k++){
        sum[k]=Find(n,k);//求扩精度后的最大的x 
        if(sum[k])sum[k]--;//1特判 
        if(sum[k]==0){Max=k;break;}
    }
    //容斥 
    for(int j=Max;j>=2;j--)
        for(int i=1;i<j;i++)
            if(j%i==0)sum[i]-=sum[j];
    for(int i=1;i<=Max;i++)res+=sum[i]*i;
    return res; 
}

int main(){
    ll L,R;
    while(cin>>L>>R && R)
        cout<<solve(R)-solve(L-1)<<endl;
}
原文地址:https://www.cnblogs.com/zsben991126/p/10859871.html