题目链接:bzoj2818
—————————————-
概述
题目大意如下。
给定一个正整数n,求满足
1≤x,y≤n
,且
gcd(x,y)
为质数的数对
(x,y)
有多少个。(
1≤n≤10000000
)
注:数对 (2,4) 和数对 (4,2) 是不同的数对.
—————————————-
题解
记答案为Ans,那么有:
假如我们要对上面那个式子进行化简,那么抱歉,我是没有那么高的水平。那怎么解决这个问题呢?
我们不妨换个角度思考,既然题目想统计的是 gcd(x,y) 为质数的数对,那么我们可以枚举1~n之间的每一个质数 pi ,分别计算每个 pi 的贡献,也就是计算每个 pi 是多少对数的最大公约数。
我们假设
a、b
的最大公约数是质数
p
,那么我们可以把
其中,
a1
与
b1
一定互质,否则
a
和
由于我们枚举的是
由于
我们可以通过线性筛求得1~ n 中所有数的欧拉函数,记录前缀和来求得上式的值。
由于题目里的规定:
还有一个细节需要注意:数对 (pi,pi) 的最大公约数也是 pi ,上面所给的计算式中将结果乘了2,也就是将数对 (x,y) 交换位置变成 (y,x) 。然而 (pi,pi) 交换之后还是 (pi,pi) ,所以需要将这一部分多计算的答案减去,也就是减去1~n之间质数的个数。
综上:
—————————————-
代码
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#define ll long long
#define For(i,j,k) for(register int i=j; i<=(int)k; ++i)
#define Forr(i,j,k) for(register int i=j; i>=(int)k; --i)
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 10000005;
int n, tot;
int pri[maxn/10], phi[maxn];
ll Ans;
ll sum[maxn];
bool vis[maxn];
inline void init(int n){
sum[1] = phi[1] = 1;
For(i, 2, n){
if(!vis[i]){
pri[++tot] = i;
phi[i] = i-1;
}
sum[i] = sum[i-1]+phi[i];//记录前缀和.
for(register int j=1,x; j<=tot&&(x=i*pri[j])<=n; ++j){
vis[x] = true;
if(i%pri[j] == 0){
phi[x] = phi[i]*pri[j];
break;
}
else phi[x] = phi[i]*(pri[j]-1);
}
}
}//线性筛.
int main(){
scanf("%d", &n);
init(n);//线性筛求得质数及欧拉函数.
Ans = 0;
For(i, 1, tot)
Ans += sum[n/pri[i]];//统计答案.
Ans = (Ans<<1)-tot;//减去重复计算的部分.
printf("%lld", Ans);
return 0;
}
—————————————-
小结
本题难点在于概念转换,将统计数对贡献变成统计质数贡献,后者由于有更多的性质可以利用,比如可以转化成欧拉函数,所以更方便解题。
—————————————-
——wrote by miraclejzd