【HDU-3208-Integer’s Power】容斥原理+DP

33 篇文章 0 订阅
24 篇文章 0 订阅

HDU-3208-Integer’s Power
题意就是定义 power(x) p o w e r ( x ) x x 能被表示为nk中最大的 k k ,例如power(9)=2,power(32)=5,power(18)=1,给出 l,r l , r 计算

Σri=lpower(i) Σ r i = l p o w e r ( i )

很明显可以将题意转化为
Σri=1power(i)Σl1i=1power(i) Σ r i = 1 p o w e r ( i ) − Σ l − 1 i = 1 p o w e r ( i )

由于这题要求可以被表示的最大次幂,我们就不能像上一题一样容斥,因为某些数表示为 m4 m 4 比表示为 (m2)2 ( m 2 ) 2 更优,所以我们要先算出所有数作为次幂可能的出现次数,由于 260>1018 2 6 0 > 10 1 8 所以我们只需要算到60即可,在预处理出这个数组之后,我们就可以进行容斥,这道题要怎么容斥呢,我们想一下比如我们计算6作为次幂出现的次数为 dp[6] d p [ 6 ] ,那么这之中肯定已经包含了 dp[2] d p [ 2 ] dp[3] d p [ 3 ] 中的一部分,相对于2,3,我们肯定选择6,因为他更大,所以我们要让 dp[2]=dp[6],dp[3]=dp[6] d p [ 2 ] − = d p [ 6 ] , d p [ 3 ] − = d p [ 6 ] ,很明显我们要对某个数的所有因子项减去当前数的出现次数,怎么保证每个数组做贡献的时候是最终状态呢,我们只要逆序去dp就可以了,这样就保证了每个数组去更新其他数组的时候也自己已经是最终状态。另外这题利用 pow(x,1/y) p o w ( x , 1 / y ) 去计算 xy x y 具有精度问题,所以要在求出之后左右判一下精度问题。

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
typedef long long ll;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll dp[63];
ll pow_(ll x,int n)
{
    ll ans=1;
    while(n)
    {
        if(n&1)
        {
            double tmp=1.0*INF/ans;
            if(x>tmp) return -1;
            ans*=x;
        }
        n>>=1;
        if(x>((ll)1<<31)&&n) return -1;
        x=x*x;
    }
    return ans;
}
ll cal(ll x,ll n)//返回精确的\sqrt[y]{x}
{
    ll a=(ll)pow(x,1.0/n);
    ll qt=pow_(a,n);
    ll ql=pow_(a-1,n);
    ll qr=pow_(a+1,n);
    if(qr!=-1&&qr<=x) return a+1;
    else if(qt!=-1&&qt<=x) return a;
    else if(ql!=-1&&ql<=x) return a-1;
}
ll solve(ll n)
{
    if(n==1) return 1;
    int i,j;
    memset(dp,0,sizeof(dp));
    dp[1]=n;
    for(i=2;i<63;i++)
    {
        dp[i]=cal(n,i)-1;
        if(dp[i]==0) break;
    }
    int k=i;
    for(i=k-1;i>0;i--)//逆序DP
    {
        for(j=1;j<i;j++)
        {
            if(i%j==0) dp[j]-=dp[i];
        }
    }
    ll ans=0;
    for(int i=1;i<k;i++) ans+=1LL*i*dp[i];
    return ans;
}
int main()
{
    ll l,r;
    while(scanf("%lld%lld",&l,&r)!=EOF)
    {
        if(l==0&&r==0) break;
        printf("%lld\n",solve(r)-solve(l-1));
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值