AcWing 220 最大公约数

题目

AcWing 220

给定整数 N ,求 1x,yN107gcd(x,y) 为素数的 (x,y) 有多少对

分析

用朴素的算法求解显然复杂度为 O(N2log2N) ,所以考虑变形:

x=1Ny=1N[gcd(x,y)P]=kPx1=1Nky1=1Nk[x1y1]

观察后两个和式,可以发现下面这个与它形式相近的式子能转化为欧拉函数的形式:

x1=1Nky1=1x1[x1y1]=x1=1Nkφ(x1)

将上式乘 2 ,可以发现它与最开始的式子相比,多加了 x1=y1[x1y1] 的部分,此时 x1=y1=1 ,设集合 P 表示小于等于 N 的素数,那么多加的次数就等于 |P| ,所以原式可以化为:

2kPx1=1Nkφ(x1)|P|=2kPsum(Nk)|P|

其中 sum(x) 表示欧拉函数的 x 项前缀和

那么我们可以用线性筛以 O(N) 同时筛出素数和欧拉函数,并以 O(N) 计算前缀和,所以时间复杂度为 O(N)

代码

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

const int MAX_N = 10000000 + 5;
int n, cnt = 0;
bool is_prime[MAX_N];
int prime[MAX_N], phi[MAX_N];
long long sum[MAX_N];

void sieve(int n)
{
    memset(is_prime, 1, sizeof(is_prime));
    cnt = 0;
    is_prime[1] = false;
    phi[1] = 1;
    for(int i = 2; i <= n; i++) {
        if(is_prime[i]) {
            prime[++cnt] = i;
            phi[i] = i - 1;
        }
        for(int j = 1; j <= cnt && prime[j] * i <= n; j++) {
            int t = prime[j] * i;
            is_prime[t] = false;
            if(i % prime[j]) {
                phi[t] = phi[i] * phi[prime[j]];
            } else {
                phi[t] = phi[i] * prime[j];
                break;
            }
        }
    }
}

int main()
{
    cin >> n;
    sieve(n);
    long long ans = 0;
    sum[0] = 0;
    for(int i = 1; i <= n; i++)
        sum[i] = sum[i - 1] + phi[i];
    for(int i = 1; i <= cnt; i++)
        ans += 2ll * sum[n / prime[i]];
    ans -= cnt;
    cout << ans << endl;
    return 0;
}
posted @   f(k(t))  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示