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);
}

猜你喜欢

转载自www.cnblogs.com/henry-1202/p/10222791.html