题目传送门:【BZOJ 2818】
题目大意:输入整数 N,求 1 ≤ x , y ≤ N 且 gcd ( x , y ) 为质数的数对 ( x , y ) 的数目。其中 1 ≤ N ≤ 107 。
题目分析:
由题,欲求 gcd ( x , y ) = p(1 ≤ x , y ≤ N,p 为质数),根据 gcd 的性质,
我们可将其转化一下,变成:求 gcd ( x , y ) = 1(1 ≤ x , y ≤
⌊Np⌋
)。
令 ⌊Np⌋ = d,设此时的答案为 ans pi ;在 1 ≤ x , y ≤ ⌊Np⌋ 这个范围内,根据欧拉函数 φ 的定义(与这个数互质的数的数量),可知:ans d = 2 * ( φ(d) + φ(d-1) + φ(d-2) + …… + φ(2) + φ(1) ) - 1。
对于上式,由于在数对 ( x , y ) 中,x 和 y 可以交换,因此答案要乘以 2;又因为对于 ( 1 , 1 ) 只有一种情况,所以最后的答案要减 1。
之后,我们对每个不大于 N 的质数进行类似的处理,最后得到的答案 ans = ∑anspi (所有的 pi 不大于 N)。
因此,我们只需要预处理出 107 以内的所有质数,所有数的 φ 值,以及 φ 值的前缀和,枚举每个质数 pi 并求出对应的 anspi ,最后求和即可(前缀和&最后的答案要开 long long)。
下面附上代码:
- #include<cstdio>
- const int MX=10000005;
- int n,ptot=0,prime[MX],phi[MX]; //ptot:质数总数
- bool isnot[MX];
- long long ans=0,sum_phi[MX]; //sum_phi: φ的前缀和
- void sieve(int n){
- isnot[1]=true;
- phi[1]=1;
- for (register int i=2;i<=n;i++){
- if (!isnot[i]){
- prime[++ptot]=i;
- phi[i]=i-1;
- }
- for (int t=1;t<=ptot;t++){
- int j=prime[t]*i;
- if (j>n) break;
- isnot[j]=true;
- phi[j]=phi[prime[t]]*phi[i];
- if (i%prime[t]==0){
- phi[j]=prime[t]*phi[i];
- break;
- }
- }
- }
- }
- int main(){
- scanf(”%d”,&n);
- sieve(n);
- for (register int i=1;i<=n;i++) //预处理 φ的前缀和
- sum_phi[i]=sum_phi[i-1]+phi[i];
- for (register int i=1;i<=ptot;i++){ //枚举每个质数 pi并求出此时的 ans
- int d=n/prime[i];
- ans+=sum_phi[d]*2-1;
- }
- printf(”%lld”,ans);
- return 0;
- }