【洛谷P2568】GCD

题目大意:给定整数 \(N\),求\(1\le x,y\le N\)\(gcd(x,y)\) 为素数的数对 \((x,y)\) 有多少对。

题解:

\[\sum_{p \in \text { prime }} \sum_{i=1}^{n} \sum_{j=1}^{n}[\operatorname{gcd}(i, j)=p] \]

\[\sum_{p \in \text { prime }} \sum_{i=1}^{\left\lfloor\frac{n}{p}\right\rfloor} \sum_{j=1}^{\left\lfloor\frac{n}{p}\right\rfloor}[\operatorname{gcd}(i, j)=1] \]

\[\sum_{p \in \text { prime }}\left(\sum_{i=1}^{\left\lfloor\frac{n}{p}\right\rfloor}\left(2 \sum_{j=1}^{i}[\operatorname{gcd}(i, j)=1]\right)-1\right) \]

\[\sum_{p \in \text { prime }}\left(2 \sum_{i=1}^{\left\lfloor\frac{n}{p}\right\rfloor} \varphi(i)-1\right) \]

因此,利用线性筛求出欧拉函数的前缀和,直接枚举素数计算答案贡献即可。

代码如下

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e7+10;

int n,prime[maxn],tot;
ll phi[maxn],sum[maxn];
bool vis[maxn];

void sieve(){
    phi[1]=1;
    for(int i=2;i<=n;i++){
        if(!vis[i])prime[++tot]=i,phi[i]=i-1;
        for(int j=1;i*prime[j]<=n;j++){
            vis[i*prime[j]]=1;
            if(i%prime[j]==0){
                phi[i*prime[j]]=phi[i]*prime[j];
                break;
            }else{
                phi[i*prime[j]]=phi[i]*(prime[j]-1);
            }
        }
    }
    for(int i=1;i<=n;i++)sum[i]=sum[i-1]+phi[i];
}

int main(){
    scanf("%d",&n);
    sieve();
    ll ans=0;
    for(int i=1;i<=tot;i++)ans+=(2*sum[n/prime[i]]-1);
    printf("%lld\n",ans);
    return 0;
}
posted @ 2019-04-17 16:54  shellpicker  阅读(166)  评论(0编辑  收藏  举报