1. 程式人生 > >BZOJ 2818 Gcd(尤拉函式+質數篩選)

BZOJ 2818 Gcd(尤拉函式+質數篩選)

2818: Gcd

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 9108  Solved: 4066
[Submit][Status][Discuss]

Description

給定整數N,求1<=x,y<=N且Gcd(x,y)為素數的
數對(x,y)有多少對.

 

Input

一個整數N

Output

如題

Sample Input

4
 

Sample Output

4

HINT

 

hint

對於樣例(2,2),(2,4),(3,3),(4,2)

1<=N<=10^7

 

題解:gcd(x,y)=prime[i];
gcd(x/prime[i],y/prime[i])=1;
求每個數的尤拉值再用字首和記錄該數前所有互質對數sum[i];
列舉1~n/prime[i]有多少對互質對數然後將x,y互換的情況加起來即可(注:gcd(1,1)出現兩次,所以要減1次)

#include<iostream>
#include<stdio.h>
#define ll long long
using namespace std;
const int maxn=1e7+7;
bool mark[maxn];
ll prime[maxn],phi[maxn],sum[maxn];
void eular(int n){//線性篩選求尤拉值
        int cnt=0;
        phi[1]=1;
        for(int i=2;i<=n;i++){
                if(!mark[i])
                        prime[cnt++]=i,phi[i]=i-1;
                for(int j=0;j<cnt&&i*prime[j]<=n;j++){
                        mark[i*prime[j]]=1;
                        if(i%prime[j])//互質
                                phi[i*prime[j]]=phi[i]*phi[prime[j]];
                        else{//不互質
                                phi[i*prime[j]]=phi[i]*prime[j];//原因:該質數已存在則不用乘(1-1/prime[j]);
                                break;//防止重複增加時間
                        }
                }
        }
}
int main()
{
        int n;
        scanf("%d",&n);
        eular(n);
        ll ans=0;
        for(int i=1;i<=n;i++)//i之前所有互質對數
                sum[i]=sum[i-1]+phi[i];
        for(int i=0;prime[i]&&prime[i]<=n;i++)//gcd(x/prime[i],y/prime[i])=1,x,y互換並減去(1,1)重複的情況
                ans+=sum[n/prime[i]]*2-1;
        printf("%lld\n",ans);
        return 0;
}