令S[n] = sum{gcd(i,j) | 1<=i<n, 1<=j<=n},给出n(1<n<4000001),计算S[n]。
令f[n] = gcd(1,n)+gcd(2,n)+……+gcd(n-1,n),则S[n] = f[1]+f[2]+f[3]+……+f[n];
令g(n,i)为gcd(x,n) == i 的 x 的个数,则f[n] = sum{i*g(n,i) | n % i == 0};
gcd(x,n) <==> gcd(x/i, n/i) == 1,所以g(n,i) <==> phi(n/i) (phi(x)为欧拉函数,表示小于x的与x互质的数的个数);
用筛法计算phi[n];
用类似于DP的方法计算f[n]:枚举 i ,更新 i 的倍数f[i*x]。
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
typedef long long ll;
const int maxn = 4000001;
int f[maxn];
int phi[maxn+1];
ll s[maxn];
int euler_phi_table(int n)
{
for (int i=2; i<=n; i++) phi[i] = 0;
phi[1] = 1;
for (int i=2; i<=n; i++)
if (!phi[i])
{
for (int j=i; j<=n; j+=i)
{
if (!phi[j])
phi[j] = j;
phi[j] = phi[j] / i * (i-1);
}
}
}
void init()
{
euler_phi_table(maxn);
for (int i=1; i<maxn; i++)
{
int j = i * 2;
while (j <= maxn)
{
f[j] += i * phi[j/i];
j += i;
}
}
s[2] = f[2];
for (int i=3; i<maxn; i++) s[i] = s[i-1] + f[i];
}
int main()
{
init();
int n;
while (scanf("%d",&n) && n)
{
printf("%lld\n",s[n]);
}
return 0;
}