zoukankan      html  css  js  c++  java
  • hdu3208 Power of Integer

    /**
    题目:H - Power of Integer
    链接:https://vjudge.net/contest/152887#problem/H
    题意:给出区间[a, b],问你区间[a, b]所有数的幂的和是多少,定义一个
    数的幂是这样的:对于一个数y,存在一个最小的数x,有一个最大的k,使得x^k=y,
    那k就是y的幂
    
    思路:看到这种题目容易想到[2,b]-[2,a-1]的做法;
    先对[2,b]考虑,因为[2,a-1]同它做法是一样的。一开始我想的是先求x^2<=b 求出最大的满足条件的x,
    然后可以计算有多少个幂为2的数在该范围[2,x]内的数的2次方都在[2,b]内。然后再计算x^3<=b的最大的x;然后x^4,x^5,,,,到找不到为止。
    但是这样做无法处理重复的情况。
    我当时想了很久还是想不到,实在想不到。问了志轩的想法。
    他说从高位到低位处理。
    假设我求x^12<=b;那么得出一个区间[2,x]范围内的数。对其中某一个数xx^12<=b而这个xx^12是可以分解为(xx^6)^2,(xx^4)^3,(xx^3)^4,(xx^2)^6,(xx^12)^1;
    这些可以划分的次方是原来数的约数,不包括本身12;这样就可以提前减去他们产生的贡献。随着幂减少,之后加到他们也不会有影响,因为已经减过了。
    
    代码没有ac,精度问题。
    最下面那份代码ac了。
    
    另外做法:利用计算器预处理所有x^2<=10^18,x^3<=10^18,x^4<=10^18....的x值。然后计算的时候就可以控制好,防止溢出了。
    
    */
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    const double eps = 1e-6;
    const int maxn = 1e9+100;
    int sum[64];
    ll a, b;
    //预处理每个数的约数和(不包括自身)
    void getSum()
    {
        sum[1] = 0;
        for(int i = 2; i <= 63; i++){
            for(int j = 1; j*2 <= i; j++){
                if(i%j==0) sum[i]+=j;
            }
        }
        //test
        //printf("%d %d
    ",sum[8],sum[9]);
    }
    //判断是否满足x^y<=b
    bool judge(ll x,int y,ll b)
    {
        ll res = 1;
        for(int i = 1; i <= y; i++){
            if(log(res)+log(x)-log(b)>=eps) return false;
            res *= x;
        }
        //return true;
        return res<=b;
    }
    //计算[2,x]范围内的power sum。
    ll solve(ll y)
    {
        if(y<2) return 0;
        //求x^i<=y的x值。
        ll ans = 0;
        for(int i = 63; i >= 1; i--){
            ll mas = 1;
            ll lo = 1, hi = b, m;
            while(lo<=hi){
                m = (lo+hi)/2;
                if(judge(m,i,y)){//满足条件
                    lo = m+1; mas = max(mas,m);
                }else
                {
                    hi = m-1;
                }
            }
            //if(mas==1) continue;
            //cout<<"mas = "<<mas<<endl;
            //cout<<"i = "<<i<<endl;
            ans += 1LL*(i-sum[i])*(mas-1);
        }
        return ans;
    }
    int main()
    {
        getSum();
        while(scanf("%lld%lld",&a,&b)==2&&a){
          //  ll p = 1;
           // a = b = p<<59;///
          //  b+=2;
            //cout<<"a = "<<a<<endl;
            //cout<<"solve(b) = "<<solve(b)<<endl;
            //cout<<"solve(a-1) = "<<solve(a-1)<<endl;
            printf("%lld
    ",solve(b)-solve(a-1));
        }
        return 0;
    }
    
    
    
    
    
    ///from zzx
    /*
    from zzx
    思路:由于要找的幂尽量大,所以我们要从高到低来枚举计算幂的贡献
           对于幂k 二分一下范围[x,y] 满足a <= x ^ k <= y ^ k <= b
           现在就是要去重了
           举个例子
           2^4 等于16 4^2也等于16 所以算完k为4的贡献后(假设为d),要除掉对k的所有约数的影响(显然影响也为d)
    ps: 被 long long 相乘时判溢出 坑了几波,还是取对数靠谱一些
    */
    /*
    #include<bits/stdc++.h>
    #define LL long long
    using namespace std;
    const double eps = 1e-6;
    LL a, b,cnt[64];
    bool check1(LL x,int y)
    {
        LL res = 1;
        for(int i = 1; i <= y; i++)
        {
            if(log(res) + log(x) - log(a) >= eps) return true;
            res *= x;
        }
        return res >= a;
    }
    LL FindL(int y)
    {
        LL l = 1,r = b,ans = b;
        while(l <= r)
        {
            LL mid = (l + r) / 2;
            if(check1(mid,y)) ans = min(ans,mid),r = mid - 1;
            else l = mid + 1;
        }
        return ans;
    }
    bool check2(LL x,int y)
    {
        LL res = 1;
        for(int i = 1; i <= y; i++)
        {
            if(log(res) +log(x) - log(b) > eps) return false;
            res *= x;
        }
        return res <= b;
    }
    LL FindR(int y)
    {
        LL l = 1,r = b,ans = 1;
        while(l <= r)
        {
            LL mid = (l + r) / 2;
            if(check2(mid,y)) ans = max(ans,mid),l = mid + 1;
            else r = mid - 1;
        }
        return ans;
    }
    
    int main()
    {
        while(scanf("%lld%lld",&a,&b)&&(a+b))
        {
            memset(cnt,0,sizeof(cnt));
            LL ans = 0;
            for(int i = 63; i >= 1; i--)
            {
                LL x = FindL(i), y = FindR(i);
                LL d = max(y-x+1,0LL);
                if(d)
                {
                    d -= cnt[i];
                    ans += i * d;
                    if(d)
                    {
                        for(int k = 1; k * k <= i; k++)
                        {
                            if(i % k == 0){
                                cnt[k] += d;
                                if(i/k!=k) cnt[i/k]+=d;
                            }
                        }
                    }
                }
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }
    */
    View Code
  • 相关阅读:
    Activity的生命周期和启动模式
    网络基础1
    Jersey用户指南学习笔记1
    IntelliJ IDEA + Maven + Jetty + Jersey搭建RESTful服务
    Java并发编程实战笔记—— 并发编程4
    Java并发编程实战笔记—— 并发编程3
    Java并发编程实战笔记—— 并发编程2
    7.2.4
    7.2.4
    7.2.3
  • 原文地址:https://www.cnblogs.com/xiaochaoqun/p/6556346.html
Copyright © 2011-2022 走看看