51Nod 1188 - 最大公约数之和 V2(欧拉函数)

题目链接 https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1188

【题目描述】
给出一个数N,输出小于等于N的所有数,两两之间的最大公约数之和。相当于求

Ans=i=1i<nj=i+1j<ngcd(i,j)

Input
第1行:1个数T,表示后面用作输入测试的数的数量。(1 <= T <= 50000)
第2 - T + 1行:每行一个数N。(2 <= N <= 5000000)
Output
共T行,输出最大公约数之和。

Input示例
3
10
100
200000
Output示例
67
13015
143295493160

【思路】
51Nod 1040是这题的简化版,求的是

i=1i<ngcd(i,n)
有公式
i=1i<ngcd(i,n)=i=1,i|ni<ni×phi(ni)
把它用到这道题里面去
Ans=i=1nj=1n1gcd(i,j)
=i=2i<nj=1j<igcd(i,j)
=i=2i<nt=1,t|it<it×phi(it)
按照这个式子去计算,当然不能直接像这样直接枚举每个 i,然后再枚举每个 i 的因子 t ,通过观察可以发现这个式子枚举的是 [2,n] 所有数除了自己之外的所有因子,所以换个角度出发,直接枚举每个因子 t,然后把 t 的若干倍作为 i 来使用,就可以像埃氏筛那样在 O(nlogn) 的时间内预处理所有答案了

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn = 5000005;

int phi[maxn];
ll ans[maxn];

void 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 (0 == phi[i]) {
            for (int j = i; j <= n; j += i) {
                if (0 == phi[j]) phi[j] = j;
                phi[j] = phi[j] / i * (i - 1);
            }
        }
    }
    for(int i=1;i<=n;++i){
        for(int j=2;j*i<=n;++j){
            ans[i*j]+=(ll)phi[j]*(ll)i;
        }
    }
    for(int i=1;i<=n;++i) ans[i]+=ans[i-1];
}

int main(){
    phi_table(maxn-1);
    int T;
    scanf("%d",&T);
    while(T--){
        int n;
        scanf("%d",&n);
        printf("%lld\n",ans[n]);
    }
    return 0;
}
posted @ 2018-09-04 20:05  不想吃WA的咸鱼  阅读(183)  评论(0编辑  收藏  举报