Hdu 2204 - Eddy's爱好(容斥)

题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=2204

题目大意:
给定一个数N,求1-N内有多少个数可以表示成 MK 的形式,其中K>1

分析:
可以枚举指数进行容斥,指数最多63次, 263>1018 ,然后对于含有奇数个质因子的指数加入,含有偶数个的减去,对于含有重复质因子的指数会被其他更小的指数覆盖,不用考虑

当前寻找的指数为k,也就是要找最大的满足 akn 的数,由于上界为 1018 ,所以在求 ak 的过程中很容易就会爆long long ,所以最好再加个限制,由于没想到什么好的办法,我打了一个表来加上限制

代码:

//#include<bits/stdc++.h>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long ll;

const ll mod = 1e9+7;

int up[100];

ll n;

int divide(int x)
{
    int cnt =0;
    for (int i = 2 ; i *i <= x ; i ++)
    {
        if (x%i==0)
        {
            cnt++;
            x /= i;
            if (x%i==0)
                return -1;
        }
    }
    if (x!=1)
        cnt++;
    return cnt;
}

void init()
{
    up[2] = 1000000000;
    up[3] = 1000000;
    up[5] = 3981;
    up[6] = 1000;
    up[7] = 372;
    up[10] = 63;
    up[11] = 43;
    up[13] = 24;
    up[14] = 19;
    up[15] = 15;// 437893890380859375
    up[17] = 11;// 505447028499293771
    up[19] = 8;// 144115188075855872
    up[21] = 7;// -1261475310744950487
    up[22] = 6;// 131621703842267136
    up[23] = 6 ;//789730223053602816
    up[26] = 4;// 4503599627370496
    up[29] = 4;// 288230376151711744
    up[30] = 3 ;//205891132094649
    up[31] = 3 ;//617673396283947
    up[33] = 3 ;//-2080022246165795451
    up[34] = 3 ;//0
    up[35] = 3 ;//0
    up[37] = 3 ;//-8741818693953543755
    up[38] = 2 ;//274877906944
    up[39] = 2 ;//549755813888
    up[41] = 2;// 0
    up[42] = 2 ;//0
    up[43] = 2 ;//0
    up[46] = 2 ;//70368744177664
    up[47] = 2 ;//140737488355328
    up[51] = 2;// 0
    up[53] = 2 ;//0
    up[55] = 2 ;//0
    up[57] = 2;// 144115188075855872
    up[58] = 2 ;//288230376151711744
    up[59] = 2 ;//0
}


ll quickpow(ll a, ll n)
{
    ll res= 1;
    while (n)
    {
        if (n&1)    res= res*a;
        a = a*a;
        n >>=1;
    }
    return res;
}


ll solve(ll x)
{
    ll rt = (ll)pow(n*1.0,1.0/x);
    if (quickpow(rt,x)<=n)
    {
        while (quickpow(rt+1,x)<=n&&rt+1<=up[x])
            rt ++;
    }
    else
    {
        while (quickpow(rt,x)>n)
            rt--;
    }
    return rt-1;
}

int main(){
    init();
    int T,t=1;
    while (~scanf("%lld",&n))
    {
        ll ans = 0;
        for (int i = 2 ; i <= 63 ; i ++)
        {
            if (quickpow(2,i)>n)
                break;
            int cnt = divide(i);
            if (cnt==-1) continue;
            if (cnt&1)  ans += solve(i);
            else ans -= solve(i);
        }
        printf("%lld\n",ans+1);

    }



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值