序列上的旅行 ~ Journey on A Sequence【莫比乌斯反演】【莫队】

题意简述:给定序列\(a\)\(q\)次询问在\([l1, r1],[l2, r2]\)中分别选择一个数,满足两个数互质的方案数。\(1\leq n\leq 2\times 10^4, 1\leq q\leq 2\times 10^5, 1\leq a_1, a_2, \ldots, a_n\leq 5\times 10^4\),保证数据随机。


每个询问可以拆成四个前缀和容斥的形式,现在只需考虑在\([1, x], [1, y]\)中分别选数的方案数。

\([1, x]\)\(i\)出现了\(b_i\)次,\([1, y]\)\(i\)出现了\(c_i\)次,则答案为:\(\sum_{i=1}^{\max a} \sum_{j=1}^{\max a} [\gcd(i, j)=1] b_i\cdot c_j\)

经过一些简单的莫比乌斯反演,得到\(\sum_{d=1}^{\max a}\mu(d)\sum_{i=1}^{\lfloor\frac{\max a}{d}\rfloor}b_i \sum_{j=1}^{\lfloor\frac{\max a}{d}\rfloor}c_j\)

\(E_x=\sum_{i=1}^{\lfloor\frac{\max a}{i}\rfloor} b_{i\cdot x}, F_x=\sum_{i=1}^{\lfloor\frac{\max a}{i}\rfloor} c_{i\cdot x}\),则答案为\(\sum_{d=1}^{\max a}\mu(d)\cdot E_d\cdot F_d\)

注意到\(b_i\)\(c_i\)增加时,会产生变化的\(E_j\)\(F_j\)满足\(j|i\),即只有\(\omega(i)\)项会改变,莫队维护即可。对于随机数据,复杂度期望为\(O(n\sqrt{m}\log \max a)\),注意调整块大小为\(\frac{n}{\sqrt{m}}\)

然而这题被毒瘤$搞了,现在莫队都过不去

#include <bits/stdc++.h>
#define R register
#define ll long long
using namespace std;
const int N = 21000, M = 810000, A = 51000;

int n, q, a[N], e1[A], e2[A], bel[N], mu[A], ispr[A], prime[N], nopr, num;
ll ans[M], sum;
struct node {
    int x, y, ind;
    node (int x = 0, int y = 0, int i = 0) : x(x), y(y), ind(i) {}
    inline bool operator < (const node &a) const {
        if (bel[x] == bel[a.x])
            return (bel[x] & 1) ? y < a.y : y > a.y;
        return bel[x] < bel[a.x];
    }
}que[M];
vector<int> fac[A];

template <class T> inline void read(T &x) {
    x = 0;
    char ch = getchar(), w = 0;
    while (!isdigit(ch)) w = (ch == '-'), ch = getchar();
    while (isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
    x = w ? -x : x;
    return;
}

void preCalc(int n) {
    mu[1] = 1;
    for (R int i = 2; i <= n; ++i) {
        if (!ispr[i])
            prime[++nopr] = i, mu[i] = -1;
        for (R int j = 1, k; j <= nopr && (k = i * prime[j]) <= n; ++j) {
            ispr[k] = 1;
            if (i % prime[j] == 0) break;
            mu[k] = mu[i] * -1;
        }
    }
    for (R int i = 1; i <= n; ++i)
        for (R int j = 1, k; (k = i * j) <= n; ++j)
            fac[k].push_back(i);
    return;
}

void add(int pos, int *mod, int *que) {
    int c = a[pos];
    for (R int i = 0, sz = fac[c].size(); i < sz; ++i) {
        sum += mu[fac[c][i]] * que[fac[c][i]];
        ++mod[fac[c][i]];
    }
    return;
}

void del(int pos, int *mod, int *que) {
    int c = a[pos];
    for (R int i = 0, sz = fac[c].size(); i < sz; ++i) {
        sum -= mu[fac[c][i]] * que[fac[c][i]];
        --mod[fac[c][i]];
    }
    return;
}

int main() {
    int l1, r1, l2, r2;
    read(n);
    int maxA = 0;
    for (R int i = 1; i <= n; ++i)
        read(a[i]), maxA = max(maxA, a[i]);
    preCalc(maxA), read(q);
    int B = max(1, (int) (n / sqrt(q)));
    for (R int i = 0; i < n; ++i)
        bel[i + 1] = i / B + 1;
    for (R int i = 1; i <= q; ++i) {
        read(l1), read(r1), read(l2), read(r2);
        que[++num] = node(r1, r2, i);
        que[++num] = node(r1, l2 - 1, -i);
        que[++num] = node(l1 - 1, r2, -i);
        que[++num] = node(l1 - 1, l2 - 1, i);
    }
    sort(que + 1, que + 1 + num);
    int p1 = 0, p2 = 0;
    for (R int i = 1; i <= num; ++i) {
        while (p1 < que[i].x) add(++p1, e1, e2);
        while (p1 > que[i].x) del(p1--, e1, e2);
        while (p2 < que[i].y) add(++p2, e2, e1);
        while (p2 > que[i].y) del(p2--, e2, e1);
        if (que[i].ind > 0) ans[que[i].ind] += sum;
        else ans[-que[i].ind] -= sum;
    }
    for (R int i = 1; i <= q; ++i)
        printf("%lld\n", ans[i]);
    return 0;
}

知识兔

计算机