【莫比乌斯反演】[BZOJ2440]完全平方数

题目描述
小 X 自幼就很喜欢数。但奇怪的是,他十分讨厌完全平方数。他觉得这些
数看起来很令人难受。由此,他也讨厌所有是完全平方数的正整数倍的数。然而
这丝毫不影响他对其他数的热爱。
这天是小X的生日,小 W 想送一个数给他作为生日礼物。当然他不能送一
个小X讨厌的数。他列出了所有小X不讨厌的数,然后选取了第 K个数送给了
小X。小X很开心地收下了。
然而现在小 W 却记不起送给小X的是哪个数了。你能帮他一下吗?

包含多组测试数据。文件第一行有一个整数 T,表示测试
数据的组数。
第2 至第T+1 行每行有一个整数Ki,描述一组数据,含义如题目中所描述。

解题思路:
T ≤ 50 1 ≤ Ki ≤ 10^9
所以暴力肯定过不了
然后可以用二分看小于等于当前这个数的有多少个不是完全平方数,根据容斥原理
数量=sqrt(x)以内的1的倍数-(2^2的倍数+3^3的倍数+素数^2的倍数(sqrt(x)以内)+sqrt(x)以内任意两个素数乘积(因为2^2和3^3存在公倍数,这样的数字被减了两次)。。。。
所以选择奇数个素数的就是-偶数个素数的就是加
那么刚好和莫比乌斯函数的定义一样然后就用莫比乌斯函数来解决就行了

#include <bits/stdc++.h>
using namespace std;
const long long Up = 2000000000;
const int MAXN = 50000;
int mu[MAXN+10], prime[MAXN+10], pcnt;
bool notprime[MAXN+10];
long long GetRank(long long u){
    long long ret = 0;
    long long up = sqrt(u+0.5);
    for(int i=1;i<=up;i++){
        ret += 1LL * mu[i] * int(u/(i*i));
    }
    return ret;
}
void Init(int t){
    mu[1] = 1;
    int tmp;
    for(int i=2;i<=t;i++){
        if(!notprime[i]){
            prime[++pcnt] = i;
            mu[i] = -1;
        }
        for(int j=1;j<=pcnt&&(tmp=prime[j]*i)<=t;j++){
            notprime[tmp] = true;
            if(i % prime[j] == 0){
                mu[tmp] = 0;
                break;
            }
            mu[tmp] = -mu[i];
        }
    }
}
int main(){
    int n;
    Init(50000);
    scanf("%d", &n);
    while(n--){
        long long l = 1, r = Up, k, mid;
        scanf("%lld", &k);
        while(l < r){
            mid = (l + r) >> 1;
            if(GetRank(mid)<k){l = mid+1;}
            else r = mid;
        }
        printf("%lld\n", l);
    }

    return 0;
}

你可能感兴趣的:(莫比乌斯反演)