题目地址
题目描述
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
题解
这东西其实就是(largesum_{i=1}^nsum_{j=1}^ngcd(i,j))
但是这个求和并不好搞,我们可以转化一下变成(large[gcd(i,j)=k])的类似形式
然后可以用莫比乌斯函数的性质来搞,也可以反演
因为不会反演,所以就放一个用性质的推导(这里的除法都是整除)
[large{
egin{align*}
&sum_{i=1}^nsum_{j=1}^ngcd(i,j)\
&=sum_{d=1}^ndsum_{i=1}^nsum_{j=1}^n{[gcd(i,j)=d]}\
&=sum_{d=1}^ndsum_{i=1}^{n/d}sum_{j=1}^{n/d}{[gcd(i,j)=1]}\
&=sum_{d=1}^ndsum_{i=1}^{n/d}sum_{j=1}^{n/d}sum_{k|gcd(i,j)}mu(k)\
&=sum_{d=1}^ndsum_{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
", ans);
}