看着白书做的,方法太巧妙了。。。
不过也总算掌握了算区间GCD的方法
我们令f(n) = gcd(1, n) + gcd(2, n) + ... + gcd(n-1, n)
则对应的结果s(n) = f(2) + f(3) + ... + f(n)
所以s(n) = s(n-1)+f(n)
因此我们只需求出所有的f(n),在递推即可
取g(n, i)表示小于n且与n的gcd值等于i的数x的个数(gcd(n, x) = i)
则f(n) = 1*gcd(n, 1) + 2*gcd(n ,2) + ...
而gcd(n, x) = i即gcd(n/i, x/i) = 1
所以g(n, i) == phi(n/i)
代码如下:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define MAXN 4000001
using namespace std;
int phi[MAXN+10];
LL f[MAXN+10], s[MAXN+10];
void phi_table(int n) {
memset(phi, 0, sizeof(phi));
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);
}
}
}
return ;
}
int main(void) {
int n;
phi_table(MAXN);
memset(s, 0, sizeof(s));
memset(f, 0, sizeof(f));
for(int i=1; i<=MAXN; ++i) {
for(int j=i*2; j<=MAXN; j+=i)
f[j] += i*phi[j/i];
}
s[2] = f[2];
for(int i=3; i<=MAXN; ++i)
s[i] = s[i-1]+f[i];
while(~scanf("%d", &n) && n) {
cout << s[n] << endl;
}
}