【题解】洛谷P2398 GCD SUM
洛谷P2398 GCD SUM
题意简述
给定 \(n\),求
\[\sum_{i = 1}^n \sum_{j = 1}^n \gcd(i, j)
\]
其中 \(\gcd(i, j)\) 表示 \(i\) 和 \(j\) 的最大公约数。
-
对于 \(30\%\) 的数据,\(n\leq 3000\)。
-
对于 \(60\%\) 的数据,\(7000\leq n\leq 7100\)。
-
对于 \(100\%\) 的数据,\(n\leq 10^5\)。
思路
暴力(30pts)
复杂度 $ O(n^2) $
\(Code\)
#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) {
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 = 1; j <= n; j++) {
ans += __gcd(i, j); //自2021年以后,NOI相关赛事中允许使用下划线函数。
//需要指出的是,C++17中gcd()被加入标准,可以直接调用。
}
}
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) = k * k\) 。
-
容斥处理 :从大到小处理每个 \(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 此题。
坑点:要开 long long 谁都知道,但快写也要开!!!
\(Code\)
//C++14 (GCC 9) O2, 43ms
//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(long long x, bool is_endl) { //快写不开long long爆70pts
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 = n; d >= 1; --d) {
long long k = n / d;
cnt[d] = k * k;
}
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;
}
推荐题目
- 洛谷P1390 公约数的和
配套题解→P1390题解
双倍经验↑↑↑
\[The \ End
\]

浙公网安备 33010602011771号