【题解】洛谷P1390 公约数的和
洛谷P1390 公约数的和
题意简述
给定 \(n\),求
\[\sum_{i = 1}^n \sum_{j = i + 1}^n \gcd(i, j)
\]
其中 \(\gcd(i, j)\) 表示 \(i\) 和 \(j\) 的最大公约数。
- 对于 \(40\%\) 的数据,保证 \(n \leq 2 \times 10^3\)。
- 对于 \(100\%\) 的数据,保证 \(2 \leq n \leq 2 \times 10^6\)。
思路
暴力(40pts)
复杂度 $ O(n^2) $
\(Code\)
#include <bits/stdc++.h>
using namespace std;
long long ans;
long long My_gcd(int a, int b) {
return (b == 0) ? a : My_gcd(b, a % b);
}
inline int read() {
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + ch - 48;
ch = getchar();
}
return x * f;
}
inline void write(int x, bool is_endl) { //is_endl: 必须填写此布尔参数
if (x < 0) {
putchar('-');
x = -x;
}
if (x > 9)
write(x / 10, false);
putchar(x % 10 + '0');
if (is_endl)
putchar('\n');
}
int main() {
int n = read();
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
ans += My_gcd(i, j);
}
}
write(ans, true);
return 0;
}
数学优化
优化思路
考虑将 $ O(n^2) $ 的复杂度优化到 $ O(n\log n) $,则此复杂度可以接受 2e6 的数据量。
- 统计所有 \(\gcd(i, j) = d\) 的对数,然后乘 \(d\) ,最后求和。
- 使用容斥原理来计算每个 \(d\) 对的个数。
数学实现
不妨设 \(f(d)\) 为满足 \(\gcd(i, j) = d\) 且 \(i < j\) 的对数,那么我们可以:
- 计算 \(g(d)\) :所有满足 \(i < j\) 且 \(d \ \vert \ i\) 且 \(d \ \vert \ j\) 的对数。
令 \(k = \lfloor \frac{n}{d} \rfloor\) ,则 \(g(d) = \frac{k(k - 1)}{2}\) 。
-
容斥处理 :从大到小处理每个 \(d\) ,减去所有 \(d\) 的倍数 \(m\) 对应的 \(f(m)\) ,从而得到 \(f(d) = g(d) - \sum_{m > d, m \ \vert \ d} f(m)\) 。
-
求和 :总和为 \(\sum^{n}_{d = 1} d \times f(d)\) 。
初始化复杂度 \(O(n)\) ,容斥处理总次数为 $ O(n\log n) $,可以 AC 此题。
\(Code\)
//C++14 (GCC 9) O2, 240ms
//Powered by WaymingDev
#include <bits/stdc++.h>
using namespace std;
long long ans;
inline int read() {
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + ch - 48;
ch = getchar();
}
return x * f;
}
inline void write(int x, bool is_endl) { //is_endl: 必须填写此布尔参数
if (x < 0) {
putchar('-');
x = -x;
}
if (x > 9)
write(x / 10, false);
putchar(x % 10 + '0');
if (is_endl)
putchar('\n');
}
int main() {
int n = read();
vector<long long> cnt(n + 2, 0); // 用于存储每个 d 的对数
for (int d = 1; d <= n; d++) {
long long k = n / d;
cnt[d] = k * (k - 1) / 2;
}
for (int d = n; d >= 1; --d) {
for (int m = 2 * d; m <= n; m += d) {
cnt[d] -= cnt[m]; // 容斥处理
}
ans += (long long)d * cnt[d];
}
write(ans, true);
return 0;
}
推荐题目
- 洛谷P2398 GCD SUM
配套题解→P2398题解
双倍经验↑↑↑
\[The \ End
\]

浙公网安备 33010602011771号