【题解】 UVa P11417【GCD】

背景

发现双倍经验,看了下题目,印象中可以用莫比乌斯水过,发现真的可以用诶,开心qwq。

关于莫比乌斯反演点我点我

三倍经验:UVa11417UVa11426Luogu1390

分析

本题就是求

\sum_{i=1}^{n}\sum_{j=i+1}^{n}\gcd(i,j)

然后我们可以先求有重复元素的,然后减去重复的好了,这样方便套反演。
枚举最大公约数$d$然后转化成可以分块的形式(准确的说应该是分块思想,公共乘积优化),

\begin{aligned}\sum_{i = 1}^n\sum_{j = 1}^m\gcd(i, j)& = \sum_{d = 1}^nd\sum_{i = 1}^n\sum_{j = 1}^m[\gcd(i, j) = d]\\& = \sum_{d = 1}^nd\sum_{x=1}^{\left\lfloor\frac n d\right\rfloor}\mu(x)\left\lfloor\frac n {dx}\right\rfloor\left\lfloor\frac m {dx}\right\rfloor\end{aligned}

套分块$O((\sqrt(n)^2)=O(n)$,当然计算过程中溢出了,懒得优化什么的了,直接上了$int128$,应用$double$或高精吧。

#include<cstdio>
#include<algorithm>
using std::fill;using std::swap;
typedef unsigned long long int64;
typedef __int128 int128;
const int MAXN = 1000 + 6;
int primes[MAXN],mu[MAXN],sum[MAXN],num;
bool isPrime[MAXN];
inline int min(int a,int b){return a < b ? a : b;}
void sieve(){
    fill(isPrime,isPrime + MAXN,true);
    num = 0,mu[1] = 1;
    for(int i = 2;i < MAXN;++i){
        if(isPrime[i]) primes[num++] = i,mu[i] = -1;
        static int d;
        for(int j = 0;j < num && (d = i * primes[j]) < MAXN;++j){
            isPrime[d] = false;
            if(i % primes[j] == 0){
                mu[d] = 0;break;
            }else mu[d] = -mu[i];
        }
    }
    sum[0] = 0;
    for(int i = 1;i < MAXN;i++) sum[i] = sum[i - 1] + mu[i];
}

int64 f(int n,int m,int d){
    if(n > m) swap(n,m);
    int64 ans = 0;
    n /= d,m /= d;
    for(int i = 1,last = 1;i <= n;i = last + 1){
        last = min(n / (n / i),m / (m / i));
        ans += (int128)((sum[last] - sum[i - 1]) * (int128)(n / i) * (int128)(m / i));
    }
    return ans;
}

int main(){
    sieve();
    int64 n,ans;
    while(scanf("%llu",&n) == 1 && n){
        ans = 0;
        for(int d = 1;d <= n;d++) ans += d * f(n,n,d);
        printf("%llu\n",(ans - n * (n + 1) / 2) / 2);
    }

    return 0;
}