题意:求\(G(n)\)
$$G(n)=\sum_{1\leqslant i< j\leqslant n}\gcd (i,j)$$
设\(f(n)=\sum_{i=1}^{n-1}\gcd (i,n) \)
则\(G(n)=\sum_{i=2}^{n}f(n)\)
可得递推公式\(G(n)=G(n-1)+f(n)\)
所有\(\gcd (x, n)\)的值都是n的约数,按照约数进行分类,令\(p(n, d)\)表示满足\(\gcd (x, n) = d(x<n) \)的正整数x的个数,则\(f(n)=\sum_{d|n}p(n,d)d\)
\(\gcd (x, n) = d\)的充要条件为\(\gcd (\frac{x}{d}, \frac{n}{d}) = 1\),因此满足条件的\(\frac{x}{d}\)有\(\phi(\frac{n}{d})\)个,则\(f(n)=\sum_{d|n}\phi(\frac{n}{d})d\)
如果依次计算\(f(n)\),枚举\(f(n)\)的约数的话效率太低
因此对于每个\(d\)枚举它的倍数\(n\)并更新\(f(n)\),时间复杂度\(O(n\log (n))\)。
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
#define MAX(a,b) ((a>b)?(a):(b))
#define MIN(a,b) ((a<b)?(a):(b))
#define N 4000010
#define INF 1<<30
#define ll long long
ll euler[N];
ll sum[N];
void getEuler(){
memset(euler,0,sizeof(euler));
euler[1]=1;
for(int i=2;i<=N;++i){
if(!euler[i]){
for(int j=i;j<=N;j+=i){
if(!euler[j])
euler[j]=j;
euler[j]=euler[j]/i*(i-1);
}
}
}
}
void getSum(){
for(int i=1;i<=N;++i)
for(int j=i*2;j<=N;j+=i)
sum[j]+=euler[j/i]*i;
for(int i=3;i<=N;++i)
sum[i]+=sum[i-1];
}
int main(){
//freopen("C:\\Users\\F\\Desktop\\in.txt", "r", stdin);
//freopen("C:\\Users\\F\\Desktop\\out.txt", "w", stdout);
ll n;
getEuler();
getSum();
while(scanf("%lld",&n),n){
printf("%lld\n",sum[n]);
}
return 0;
}