Loading

P2257 YY的GCD

(默认 \(n \le m\)
设:

\[F_k = \lfloor \frac n k \rfloor \times \lfloor \frac m k \rfloor = \sum_{k|d} f_d \]

\[f_k = \sum_{k|d} \mu_{\frac d k} \times F_d = \sum_{d = 1}^{\lfloor \frac n k \rfloor} \mu_d \times F_{kd} \]

则:

\[ans = \sum_{k \in prime} f_k = \sum_{k \in prime} \sum_{d = 1}^{\lfloor \frac n k \rfloor} \mu_d \times F_{kd} \]

\(T = kd\),则有:

\[ans = \sum_{k \in prime} \sum_{d = 1}^{\lfloor \frac n k \rfloor} \mu_{\frac T k} \times F_{T} \]

至此,单次查询的时间复杂度为 \(O(\frac n {\ln n} \sqrt n)\), 总的时间复杂度就为 \(O(n + T(\frac n {\ln n} \sqrt n))\),对于 \(T \le 10^4, n \le 10^7\),显然 \(TLE\)

那么如何优化呢?

\(ans\) 的得来要枚举 \(k \in prime\)\(d\),乍一看是没得优化的,所以不妨换一种思路:枚举 \(T\)

首先考虑 \(T\) 的取值范围,易知 \(T \in [1, n]\)

再考虑 \(F_T\) 会在什么时候,以什么样的方式对答案作出贡献:

\(\forall k \in prime\)\(k | T\)\(F_T\) 会对答案作出 \(\mu_{\frac T k} \times F_T\) 的贡献。

式子也就推出来了:

\[ans = \sum_{T = 1}^n F_T \sum_{k \in prime, k | T} \mu_{\frac T k} \]

后面的 \(\sum\limits_{k \in prime, k | T} \mu_{\frac T k}\) 可以在求完 \(\mu\) 数组后 \(O(n \log \log n)\) 维护,过程跟埃氏筛相似,对于每一个素数 \(p\),将 \(f_{kp}(1 \le k \le \lfloor \frac{10^7}p \rfloor)\) 都加上 \(\mu_k\)。再做一个前缀和,套 整除分块,就能把单次查询的时间复杂度降到 \(O(\sqrt n)\),总的时间复杂度就是 \(O(n + n \log \log n + T \sqrt n)\),随便过(虽然常数不小)。


\(Code\)

#include <bits/stdc++.h>

#define MAXN 10000100

using namespace std;

typedef long long ll;

int T, n, m;
int cnt, prime[700100], mu[MAXN], pre[MAXN];
bool vis[MAXN];

template<typename _T>
void read(_T &_x) {
    _x = 0;
    _T _f = 1;
    char _ch = getchar();
    while (_ch < '0' || '9' < _ch) {
        if (_ch == '-') _f = -1;
        _ch = getchar();
    }
    while ('0' <= _ch && _ch <= '9') {
        _x = (_x << 3) + (_x << 1) + (_ch & 15);
        _ch = getchar();
    }
    _x *= _f;
}

template<typename _T>
void write(_T _x) {
    if (_x < 0) {
        putchar('-');
        _x = -_x;
    }
    if (_x > 9) write(_x / 10);
    putchar('0' + _x % 10);
}

void getmu(const int &n) {
    mu[1] = 1;
    for (int i = 2; i <= n; i++) {
        if (!vis[i]) {
            prime[++cnt] = i;
            mu[i] = -1;
        }
        for (int j = 1; j <= cnt && i * prime[j] <= n; j++) {
            vis[i * prime[j]] = 1;
            if (i % prime[j] == 0) break;
            mu[i * prime[j]] = -mu[i];
        }
    }
    for (int j = 1; j <= cnt; j++) {
        for (int i = 1; i * prime[j] <= n; i++) {
            pre[i * prime[j]] += mu[i];
        }
    }
    for (int i = 2; i <= n; i++) pre[i] += pre[i - 1];
}

ll calc(const int &n, const int &m) {
    ll res = 0;
    for (int l = 1, r; l <= n; l = r + 1) {
        r = min(n / (n / l), m / (m / l));
        res += 1ll * (pre[r] - pre[l - 1]) * (n / l) * (m / l);
    }
    return res;
}

int main() {
    getmu(1e7);
    read(T);
    while (T--) {
        read(n), read(m);
        if (n > m) write(calc(m, n));
        else write(calc(n, m));
        putchar('\n');
    }
    return 0;
}
posted @ 2022-09-24 11:10  Chy12321  阅读(18)  评论(0编辑  收藏  举报