您的位置:首页 > 其它

[bzoj2818]Gcd 欧拉函数

2017-08-25 19:58 344 查看
题目链接:bzoj2818

—————————————-

概述

题目大意如下。

给定一个正整数n,求满足1≤x,y≤n,且gcd(x,y)为质数的数对(x,y)有多少个。(1≤n≤10000000)

注:数对(2,4)和数对(4,2)是不同的数对.

—————————————-

题解

记答案为Ans,那么有:Ans=∑i=1n∑j=1n[(∑k=2gcd(i,j)[gcd(k,gcd(i,j))=1])=1].

假如我们要对上面那个式子进行化简,那么抱歉,我是没有那么高的水平。那怎么解决这个问题呢?

我们不妨换个角度思考,既然题目想统计的是gcd(x,y)为质数的数对,那么我们可以枚举1~n之间的每一个质数pi,分别计算每个pi的贡献,也就是计算每个pi是多少对数的最大公约数。

我们假设a、b的最大公约数是质数p,那么我们可以把a、b写成这样的形式:a=p×a1, b=p×b1.

其中,a1与b1一定互质,否则a和b的最大公约数就不是p了。

由于我们枚举的是p,所以已经确定了p,故我们只要统计有多少对a1、b1互质就能知道每个p有多少贡献了。

由于a、b的取值范围是1~n,所以a1、b1的取值范围均是1~np,我们可以枚举a1,计算1~a1中有多少个数与a1互质即可,也就是:∑i=1np∑j=1i[gcd(i,j)=1]=∑i=1npφ(i).

我们可以通过线性筛求得1~n中所有数的欧拉函数,记录前缀和来求得上式的值。

由于题目里的规定:(4,2)和(2,4)是不同数对,所以我们需要将这样统计的结果乘2。

还有一个细节需要注意:数对(pi,pi)的最大公约数也是pi,上面所给的计算式中将结果乘了2,也就是将数对(x,y)交换位置变成(y,x)。然而(pi,pi)交换之后还是(pi,pi),所以需要将这一部分多计算的答案减去,也就是减去1~n之间质数的个数。

综上:Ans=(2×∑i=1tot∑j=1npiφ(j))−tot.

—————————————-

代码

#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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: