bzoj2440

题目

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

题解

容易想到二分答案,故关键在于求区间[1, n]中不含平方因子的数个数,设这个函数为(f(n))

假设[1, n]中含有质数(p_1,p_2,...p_k),由容斥原理可得

[f(n)=n-(lfloorfrac{n}{p_1^2} floor+lfloorfrac{n}{p_2^2} floor+...+lfloorfrac{n}{p_k^2} floor)+(lfloorfrac{n}{p_1^2p_2^2} floor+lfloorfrac{n}{p_1^2p_3^2} floor+...+lfloorfrac{n}{p_1^2p_k^2} floor+...)-(...)+(...)-... ]

因为分母是平方数,我们可以枚举分母的值,枚举复杂度为(O(sqrt{n}))。那么前面系数怎么求?容易发现,这个系数就是莫比乌斯函数的值!质因数个数为奇数时减 和 质因数个数为偶数时加 真好对应莫比乌斯函数的((-1)^k)。根号下分母不存在含平方质因子的情况,正好对应莫比乌斯函数为零的情况。故有

[f(n)=sumlimits^{i^2le n}_{i=1}{mu(i)lfloorfrac{n}{i^2} floor} ]

也可以写成

[sumlimits^{n}_{i=1}{mu(i)^2}=sumlimits^{i^2le n}_{i=1}{mu(i)lfloorfrac{n}{i^2} floor} ]

#include <bits/stdc++.h>

#define endl '
'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define mp make_pair
#define seteps(N) fixed << setprecision(N) 
typedef long long ll;

using namespace std;
/*-----------------------------------------------------------------*/

ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
#define INF 0x3f3f3f3f

const int N = 1e5 + 10;
const double eps = 1e-5;

int mu[N];
int prime[N];
bool vis[N];
int cnt;

void init() {
    mu[1] = 1;
    for(int i = 2; i < N; i++) {
        if(!vis[i]) {
            prime[cnt++] = i;
            mu[i] = -1;
        }
        for(int j = 0; j < cnt; j++) {
            int p = prime[j];
            if(i * p > N) break;
            vis[i * p] = 1;
            if(i % p == 0) {
                mu[i * p] = 0;
                break;
            }
            mu[i * p] = -mu[i];
        }
    }
}

ll get(ll n) {
    ll res = 0;
    for(int i = 1; 1ll * i * i <= n; i++) {
        res += mu[i] * (n / (1ll * i * i));
    }
    return res;
}


int main() {
    IOS;
    init();
    int t;
    cin >> t;
    while(t--) {
        int k;
        cin >> k;
        ll l = 1, r = 1800000000;
        while(l <= r) {
            ll mid = (l + r) / 2;
            if(get(mid) >= k) {
                r = mid - 1;
            } else {
                l = mid + 1;
            }
        }
        cout << l << endl;
    }
}
原文地址:https://www.cnblogs.com/limil/p/14063968.html