bzoj2818: Gcd(尤拉函式)

Hanks_o發表於2017-10-27

題目傳送門
其實我早就看到這道題了只不過不會做。
結果今天上午我學了尤拉函式。
所以又一次看到這道題就把它做了。

解法:
對於每一個質數p,gcd(x,y)=p
我們將x和y都除以p,那麼他們的gcd就等於1了。
所以列舉每一個質數,將n除以p。
這就轉化為了在n/p中求互質的對數。

上午學的尤拉函式就派上用場了。
phi[x]表示小於等於x且跟x互質的數的個數。
那麼n/p中互質的對數就等於所有的phi[x]相加的和*2-1。
所有的phi[x]相加想必大家清楚,*2相當於兩個數調換位置。
-1相當於減去1,1多算的重複的那一次。

然後處理出phi[x]的字首和。
然後加一下每個質數的答案就行了。

程式碼實現:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
ll prime[1100000],phi[11000000];
int n,len;
void get_phi() {
    phi[1]=1;len=0;
    for(int i=2;i<=n;i++) {
        if(phi[i]==0) {
            prime[++len]=i;
            phi[i]=i-1;
        }
        for(int j=1;j<=len&&i*prime[j]<=n;j++) {
            int t=i*prime[j];
            if(i%prime[j]==0) {
                phi[t]=phi[i]*prime[j];
                break;
            }
            phi[t]=phi[i]*(prime[j]-1);
        }
    }
}
ll s[11100000];
int main() {
    scanf("%d",&n);
    get_phi();
    for(int i=1;i<=n;i++) {
        s[i]=s[i-1]+phi[i];  //字首和
    }
    ll ans=0;
    for(int i=1;i<=len;i++) {
        ans+=s[n/prime[i]]*2-1;
    }
    printf("%lld\n",ans);
    return 0;
}

相關文章