「BZOJ 2440」完全平方数「数论分块」

题意

\(T\)组数据,每次询问第\(k\)个无平方因子的数(\(1\)不算平方因子),\(T\leq 50,k\leq 10^9\)

题解

\(k\)的范围很大,枚举肯定不行,也没什么奇妙性质,于是可以想到二分。

二分的话我们需要实现一个函数\(f(n)\)表示\(n\)以内的数中无平方因子的数个数

这十分经典,相当于求\(f(n)=\sum_{i=1}^n\mu^2(i)\)

解法就是:我们考虑一个质数\(p\)\(p^2\)的倍数都不满足要求,也就是说答案得减去\(\lfloor \frac{n}{p^2}\rfloor\)。但显然这个会重复,需要容斥。就是说一个数\(x=p_1p_2..p_k\),答案得加上\((-1)^k \lfloor \frac{n}{x^2}\rfloor\)

然后就会神奇地发现对于一个正整数\(i\)它的贡献的系数恰好就是\(\mu(i)\)

也就是说答案就是\(\sum_{i=1}^{\sqrt n} \mu(i) \lfloor \frac{n}{i^2}\rfloor\) 。预处理\(\mu\)前缀和然后数论分块就好。

分块那里有一点小细节,与常见数论分块不大一样。还有二分的右边界,可以设为\(k\)\(4\)倍,实际上观察样例就可以猜到\(2\)倍就可以了。

复杂度为大致为\(O(T\sqrt k\log k)\)

#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;

typedef long long ll;

const int N = 64000;

int mu[N + 5], p[N + 5], cnt;
bool tag[N + 5];

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

ll calc(ll n) {
    ll x = (ll) sqrt(n), ans = 0;
    for(ll i = 1, j; i * i <= n; i = j + 1) {
        j = sqrt(n / (n / (i * i)));
        if(j > x) j = x;
        ans += (mu[j] - mu[i - 1]) * (n / (i * i));
    }
    return ans;
}

int main() {
    sieve(N);
    int t, k; scanf("%d", &t);
    while(t --) {
        scanf("%d", &k);
        ll l = 1, r = 4e9, mid, ans = -1;
        while(l <= r) {
            mid = (l + r) >> 1;
            if(calc(mid) >= k) r = (ans = mid) - 1;
            else l = mid + 1;
        }
        printf("%lld\n", ans);
    }
    return 0;
}

posted @ 2019-02-11 20:16  hfhongzy  阅读(117)  评论(0编辑  收藏  举报