P3327题解

我们来看一下这道题:

首先给出一个公式:

$$d(ij) = \sum\limits_{x | i}\sum\limits_{y | j} [\gcd (x, y) = 1]$$

于是,我们就要求:

$$\sum\limits_{i = 1}^n \sum\limits_{j = 1}^m \sum\limits_{x|i}\sum\limits_{y|j} [\gcd (x, y) = 1]$$

然后我们交换求和顺序,把 $x, y$ 提到前面:

$$\sum\limits_{x=1}^n\sum\limits_{y = 1}^m\left\lfloor\dfrac{n}{x}\right\rfloor\left\lfloor\dfrac{m}{y}\right\rfloor[\gcd(x, y) = 1]$$

用卷积 ($\mu$ · $id$ = $\varepsilon$) 得:

$$\sum\limits_{x = 1}^n\sum\limits_{y = 1}^m(\left\lfloor\dfrac{n}{x}\right\rfloor\left\lfloor\dfrac{m}{y}\right\rfloor \sum\limits_{i | \gcd (x, y)}\mu(i))$$

在把交换求和顺序,把 $i$ 提到前面,我们不妨设 $n \leq m$:

$$\sum\limits_{i = 1}^n\mu(i)\sum\limits_{x = 1}^{\left\lfloor\frac{n}{i}\right\rfloor}\sum\limits_{y = 1}^{\left\lfloor\frac{n}{i}\right\rfloor}\left\lfloor\dfrac{n}{xi}\right\rfloor\left\lfloor\dfrac{m}{yi}\right\rfloor$$

再提取得:

$$\sum\limits_{i = 1}^n\mu(i)\sum\limits_{x = 1}^{\left\lfloor\frac{n}{i}\right\rfloor}\left\lfloor\dfrac{n}{xi}\right\rfloor\sum\limits_{y = 1}^{\left\lfloor\frac{n}{i}\right\rfloor}\left\lfloor\dfrac{m}{yi}\right\rfloor$$

记 $f_{p} = \sum\limits_{i = 1}^p\mu(i)$,可用线性筛 $O(n)$

再记 $sum_{p} = \sum\limits_{i = 1}^p \left\lfloor\dfrac{p}{i}\right\rfloor$,数论分块 + 暴力 $O(n \sqrt{n})$

最后数论分块求结果 $O(\sqrt{n})$

$f_{p}$ 与 $sum_{p}$ 可预处理先求出

上代码环节:

#include <cstdio>
#include <iostream>
using namespace std;
#define int long long

const int maxn = 5e4;
int cnt, prime[maxn], is_prime[maxn];
int mu[maxn], sum[maxn];

void solve () {
    mu[1] = 1;
    for (int i = 2; i <= maxn; ++i) {
        if (!is_prime[i]) {
            prime[++cnt] = i; mu[i] = -1;
        }
        for (int j = 1; j <= cnt; ++j) {
            int v = i * prime[j];
            if (v > maxn) break;
            is_prime[v] = 1;
            if (i % prime[j] == 0) break;
            mu[v] = -mu[i];
        }
    } // 线性筛
    for (int i = 1; i <= maxn; ++i) {
        mu[i] += mu[i - 1];
    } // 求前缀和
    for (int i = 1; i <= maxn; ++i) {
        for (int l = 1, r; l <= i; l = r + 1) {
            r = i / (i / l);
            sum[i] += (r - l + 1) * (i / l);
        } // 数论分块
    }
}

signed main() {
    solve();
    int T; scanf ("%lld", &T);
    while (T --) {
        int n, m; scanf ("%lld%lld", &n, &m);
        if (n > m) swap (n, m);
        int ans = 0;
        for (int l = 1, r; l <= n; l = r + 1) {
            r = min (n / (n / l), m / (m / l));
            ans += (mu[r] - mu[l - 1]) * sum[n / l] * sum[m / l];
        } // 数论分块求答案
        printf ("%lld\n", ans);
    }
    return 0;
}
posted @ 2021-09-21 10:58  wangzhongyuan  阅读(14)  评论(0编辑  收藏  举报  来源