GCD

/**
题目:GCD - Extreme (II)
链接:https://vjudge.net/contest/154246#problem/O
题意:
for(i=1;i<N;i++)
    for(j=i+1;j<=N;j++)
    {
        G+=gcd(i,j);
    }
思路:
设f[n] = gcd(1,n)+gcd(2,n)+gcd(3,n)+...+gcd(n-1,n);
s[n] = f[1]+f[2]+...+f[n];
则:s[n] = f[n]+s[n-1];

f[n]的约数个数一般少于n-1个。所以如果可以以约数归类,就可以减少计算量。
设:gcd(n,i)表示 gcd(x,n) = i 时候的x的个数。(i为n的约数)
又:gcd(x,n)=i => gcd(x/i,n/i)=1;那么x/i的个数为(n/i)的欧拉函数值phi(n/i);
那么:f[n] = sum(i*phi(n/i)) (i为n的约数)
求每个f[n]不需要对每一个n单独求约数。
可以利用素数筛法类似的做法来处理。

参考思路:lrj算法经训练指南P125
*/

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxn = 4e6+100;
ll f[maxn], s[maxn];
int phi[maxn];
void phiTable()
{
    for(int i = 1; i < maxn; i++) phi[i] = i;
    for(int i = 2; i < maxn; i+=2) phi[i]/=2;
    for(int i = 3; i < maxn; i+=2){
        if(phi[i]==i){
            for(int j = i; j < maxn; j+=i){
                phi[j] = phi[j]/i*(i-1);
            }
        }
    }
}
void init()
{
    for(int i = 1; i < maxn; i++){
        for(int j = i*2; j < maxn; j+=i){
            f[j] += i*phi[j/i];
        }
    }
    for(int i = 2; i < maxn; i++) s[i] = s[i-1]+f[i];
}
int main()
{
    int T, cas=1, N;
    phiTable();
    init();
    while(scanf("%d",&N)==1&&N)
    {
        printf("%lld
",s[N]);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/xiaochaoqun/p/6653430.html