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;
}