[ exttt{Description}
]
给出 (n),求 (sumlimits_{i = 1}^nsumlimits_{j=i+1}^n gcd(i,j))。
[ exttt{Solution}
]
注意到:
[sumlimits_{i = 1}^nsumlimits_{j = i + 1}^n gcd(i, j) = frac{sumlimits_{i = 1}^nsumlimits_{j = 1}^n gcd(i, j) - sumlimits_{i = 1}^n gcd(i,i)}{2} = frac{sumlimits_{i = 1}^nsumlimits_{j = 1}^n gcd(i, j) - frac{n cdot (n + 1)}{2}}{2}
]
于是问题转化为如何求出 (sumlimits_{i = 1}^nsumlimits_{j = 1}^n gcd(i, j))。
考虑枚举 (d = gcd(i, j)),则式子化为:
[sumlimits_{d = 1}^n d cdot sumlimits_{i = 1}^nsumlimits_{j = 1}^n [gcd(i,j) = d]
]
[sumlimits_{d = 1}^n d cdot sumlimits_{i = 1}^{frac{n}{d}}sumlimits_{j = 1}^{frac{n}{d}} [gcd(i,j) = 1]
]
设 (g(n) = sumlimits_{i = 1}^nsumlimits_{j = 1}^n [gcd(i,j) = 1]),则式子化为:
[sumlimits_{d = 1} ^ n d cdot g(frac{n}{d})
]
考虑化简 (g(n)),注意到:
[varphi(n) = sumlimits_{i = 1}^n [gcd(n, i) = 1]
]
则有:
[g(n) = 2 cdot sumlimits_{i = 1}^n varphi(i) - 1
]
线性筛 (mathcal{O(n)}) 预处理出欧拉函数,再求一遍欧拉函数前缀和,即可 (mathcal{O(1)}) 计算 (g(n))。
至此 (sumlimits_{d = 1} ^ n d cdot g(frac{n}{d})) 已经可以 (mathcal{O(n)}) 计算,可以进一步用 " 数论分块 " 优化到 (O(sqrt n))。
[ exttt{Code}
]
#include <cstdio>
using namespace std;
const int N = 2000100;
int n;
int m, prime[N], fac[N];
int phi[N];
long long S[N];
void GetPrimes(int N) {
phi[1] = 1;
for (int i = 2; i <= N; i ++) {
if (!fac[i]) fac[i] = i, prime[++ m] = i, phi[i] = i - 1;
for (int j = 1; j <= m; j ++) {
if (prime[j] > fac[i] || prime[j] > N / i) break;
fac[i * prime[j]] = prime[j];
phi[i * prime[j]] = phi[i] * (i % prime[j] ? prime[j] - 1 : prime[j]);
}
}
for (int i = 1; i <= N; i ++)
S[i] = S[i - 1] + phi[i];
}
long long g(int n) {
return 2 * S[n] - 1;
}
int main() {
GetPrimes(2000000);
scanf("%d", &n);
long long ans = 0;
for (int x = 1, Nx; x <= n; x = Nx + 1) {
Nx = n / (n / x);
ans += 1ll * (x + Nx) * (Nx - x + 1) / 2 * g(n / x);
}
ans -= 1ll * n * (n + 1) / 2;
ans /= 2;
printf("%lld
", ans);
return 0;
}
[ exttt{Thanks} exttt{for} exttt{reading}
]