LuoguP2398 GCD SUM
题目地址
题目描述
for i=1 to n
for j=1 to n
sum+=gcd(i,j)
给出n求sum. gcd(x,y)表示x,y的最大公约数.
输入输出格式
输入格式:
n
输出格式:
sum
输入输出样例
输入样例#1:
复制
2
输出样例#1:
复制
5
说明
数据范围 30% n<=3000 60% 7000<=n<=7100 100% n<=100000
题解
这东西其实就是\(\large\sum_{i=1}^n\sum_{j=1}^ngcd(i,j)\)
但是这个求和并不好搞,我们可以转化一下变成\(\large[gcd(i,j)=k]\)的类似形式
然后可以用莫比乌斯函数的性质来搞,也可以反演
因为不会反演,所以就放一个用性质的推导(这里的除法都是整除)
\[\large{
\begin{align*}
&\sum_{i=1}^n\sum_{j=1}^ngcd(i,j)\\
&=\sum_{d=1}^nd\sum_{i=1}^n\sum_{j=1}^n{[gcd(i,j)=d]}\\
&=\sum_{d=1}^nd\sum_{i=1}^{n/d}\sum_{j=1}^{n/d}{[gcd(i,j)=1]}\\
&=\sum_{d=1}^nd\sum_{i=1}^{n/d}\sum_{j=1}^{n/d}\sum_{k|gcd(i,j)}\mu(k)\\
&=\sum_{d=1}^nd\sum_{k=1}^{n/d}\sum_{i=1}^{n/d/k}\sum_{j=1}^{n/d/k}\mu(k)*(n/d/k)^2\\
\end{align*}
}
\]
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e5 + 10;
int p[N], mu[N], vis[N], sum[N];
int n, cnt = 0;
ll ans = 0;
void init() {
mu[1] = 1;
for(int i = 2; i < N; ++i) {
if(!vis[i]) {p[++cnt] = i; mu[i] = -1;}
for(int j = 1; j <= cnt && p[j] * i < N; ++j) {
vis[p[j] * i] = 1;
if(i % p[j] == 0) break;
mu[i * p[j]] -= mu[i];
}
}
for(int i = 1; i < N; ++i) sum[i] = sum[i - 1] + mu[i];
}
ll calc(int n) {
ll s = 0;
for(int l = 1, r; l <= n; l = r + 1) {
r = n / (n / l);
s += (ll)((ll)(n/l) * (ll)(n/l) * (ll)(sum[r] - sum[l - 1]));
}
return s;
}
int main() {
init();
scanf("%d", &n);
for(int d = 1; d <= n; ++d) {
ans += 1ll * d * calc(n / d);
}
printf("%lld\n", ans);
}