BZOJ2818-Gcd(欧拉函数)


题解:
考虑每个质数 p 对答案的贡献,
g c d ( a p , b p ) = p 相当于 a b 互质, g c d ( x , y ) p 的数对个数即为 1 <= a , b <= n p 中互质对 a b 的个数。
a <= b ,对于 b a φ ( b ) 个取值使 a b 互质
所以符合条件的互质对 a b 个数为

i = 1 n p φ ( i )

求一下前缀和就可以了。
关于欧拉函数是什么请自行百科。
那么 φ ( n ) 怎么快速求?
考虑筛法:
若对于 i ,已知所有的 1   i φ ,枚举 <= i 的质数 p [ j ] ,可以求得 φ ( x p [ j ] )
1、若 p [ j ] x 互质, φ ( x p [ j ] ) = φ ( x ) φ ( p [ j ] )
2、若不互质,设 x = t p [ j ] k
φ ( x p [ j ] ) = φ ( t p [ j ] ( k + 1 ) ) = φ ( t ) φ ( p [ j ] ( k + 1 ) )
= φ ( t ) φ ( p [ j ] k ) p [ j ]
= φ ( x ) p [ j ]
似乎过不了 10 7 的数据,那么怎么优化呢?
我们发现,很多数被重复计算了,比如
18 = 2 9 = 3 6
60 = 2 30 = 3 20 = 4 15 = 5 12 = 6 10
是否能让每个数只被算一次呢?
伪代码

for i=2->n{
    if i是质数{
        φ(i)=i-1;
        tot=tot+1;
        p[tot]=i;
    }
    for j=1->tot{
        p[j]*i标记为合数
        计算φ(i*p[j])
        if i%p[j]==0 break;//让每个数只被其最小质因子计算到
    }
}

C o d e :

#include<bits/stdc++.h> 
#define ll long long
#define N 10000005
using namespace std;
int n,p,tot,phi[N],pri[N];
bool b[N];
ll ans,sum[N];
int main()
{
    scanf("%d",&n);
    phi[1]=1;
    for(int i=2;i<=n;i++)
    {
        if(!b[i]){phi[i]=i-1;pri[++tot]=i;}
        for(int j=1;j<=tot;j++)
        {
            int x=pri[j];
            if(i*x>n)break;
            b[i*x]=1;
            if(i%x==0){phi[i*x]=phi[i]*x;break;}
            else phi[i*x]=phi[i]*phi[x];
        }
    }
    for(int i=1;i<=n;i++)
        sum[i]=sum[i-1]+phi[i];
    for(int i=1;i<=tot;i++)
        ans+=sum[n/pri[i]]*2-1;
    printf("%lld",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_34531807/article/details/81046756