欧拉函数 - GCD(最大公约数) - 洛谷 P2568

欧拉函数 - GCD(最大公约数) - 洛谷 P2568

给 定 整 数 N , 求 1 ≤ x , y ≤ N 且 G C D ( x , y ) 为 素 数 的 数 对 ( x , y ) 有 多 少 对 。 给定整数N,求1≤x,y≤N且GCD(x,y)为素数的数对(x,y)有多少对。 N1x,yNGCD(x,y)(x,y)

G C D ( x , y ) 即 求 x , y 的 最 大 公 约 数 。 GCD(x,y)即求x,y的最大公约数。 GCD(x,y)xy

输入格式

输入一个整数N

输出格式

输出一个整数,表示满足条件的数对数量。

数据范围

1 ≤ N ≤ 1 0 7 1≤N≤10^7 1N107

输入样例:

4

输出样例:

4

分析:

问题转化:

求 g c d ( x , y ) = P < = > g c d ( x P , y P ) = 1 , 其 中 P 是 质 数 。 求gcd(x,y)=P\quad<=>\quad gcd(\frac{x}{P},\frac{y}{P})=1,其中P是质数。 gcd(x,y)=P<=>gcd(Px,Py)=1P

问 题 转 化 为 : 求 1 ≤ x , y ≤ N P 且 G C D ( x , y ) = 1 的 数 对 ( x , y ) 有 多 少 对 。 问题转化为:求1≤x,y≤\frac{N}{P}且GCD(x,y)=1的数对(x,y)有多少对。 1x,yPNGCD(x,y)=1(x,y)

求 某 段 区 间 内 , 互 质 的 点 对 的 数 量 , 参 照 : 求某段区间内,互质的点对的数量,参照: 欧拉函数 - Visible Lattice Points - POJ 3090

所 以 , 我 们 先 预 处 理 出 不 超 过 N 的 所 有 质 数 , 对 于 每 一 个 质 数 P , 都 需 要 计 算 一 遍 ∑ i = 2 N P ϕ ( i ) × 2 + 1 所以,我们先预处理出不超过N的所有质数,对于每一个质数P,都需要计算一遍\sum_{i=2}^{\frac{N}{P}}\phi(i)×2+1 NPi=2PNϕ(i)×2+1

注 意 : 本 题 需 要 单 独 加 上 y = x 上 的 点 ( 1 , 1 ) 。 注意:本题需要单独加上y=x上的点(1,1)。 y=x(1,1)

由 于 需 要 多 次 查 询 区 间 和 , 故 我 们 另 预 处 理 前 缀 和 数 组 S [ i ] = ∑ j = 1 i ϕ ( j ) , 由于需要多次查询区间和,故我们另预处理前缀和数组S[i]=\sum_{j=1}^i\phi(j), S[i]=j=1iϕ(j)

注 意 ϕ ( 1 ) = 0 , 因 为 点 ( 1 , 1 ) 是 单 独 处 理 累 加 的 。 注意\phi(1)=0,因为点(1,1)是单独处理累加的。 ϕ(1)=0(1,1)

最 终 答 案 : ∑ i = 1 c n t 2 × S [ N P i ] + 1 , 其 中 c n t 是 N 以 内 质 数 的 总 个 数 , P i 表 示 第 i 个 质 数 。 最终答案:\sum_{i=1}^{cnt}2×S[{\frac{N}{P_i}}]+1,其中cnt是N以内质数的总个数,P_i表示第i个质数。 i=1cnt2×S[PiN]+1cntNPii

代码:

#include<iostream>

#define ll long long

using namespace std;

const int N=1e7+10;

int primes[N],cnt;
bool st[N];
int phi[N];
ll s[N];

void get_prime(int n)
{
    for(int i=2;i<=n;i++)
    {
        if(!st[i])
        {
            primes[cnt++]=i;
            phi[i]=i-1;
        }
        for(int j=0;primes[j]*i<=n;j++)
        {
            st[primes[j]*i]=true;
            if(i%primes[j]==0)
            {
                phi[i*primes[j]]=phi[i]*primes[j];
                break;
            }
            phi[i*primes[j]]=phi[i]*(primes[j]-1);
        }
    }
    
    for(int i=1;i<=n;i++) s[i]=s[i-1]+phi[i];
}

int main()
{
    int n;
    cin>>n;
    get_prime(n);
    
    ll res=0;
    for(int i=0;i<cnt;i++)
    {
        int p=primes[i];
        res+=(s[n/p]*2)+1;
    }
    
    cout<<res<<endl;
    
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值