1. 程式人生 > >最大公約數之和 V2(尤拉函式)

最大公約數之和 V2(尤拉函式)

【題目描述】
給出一個數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; }