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-(\lfloor\frac{n}{p_1^2}\rfloor+\lfloor\frac{n}{p_2^2}\rfloor+...+\lfloor\frac{n}{p_k^2}\rfloor)+(\lfloor\frac{n}{p_1^2p_2^2}\rfloor+\lfloor\frac{n}{p_1^2p_3^2}\rfloor+...+\lfloor\frac{n}{p_1^2p_k^2}\rfloor+...)-(...)+(...)-... \]

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

\[f(n)=\sum\limits^{i^2\le n}_{i=1}{\mu(i)\lfloor\frac{n}{i^2}\rfloor} \]

也可以写成

\[\sum\limits^{n}_{i=1}{\mu(i)^2}=\sum\limits^{i^2\le n}_{i=1}{\mu(i)\lfloor\frac{n}{i^2}\rfloor} \]

#include <bits/stdc++.h>

#define endl '\n'
#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;
    }
}
posted @ 2020-11-30 21:43  limil  阅读(87)  评论(0编辑  收藏  举报