suxxsfe

一言(ヒトコト)

P2568 GCD

P2568 GCD

题意

给定正整数\(n\),求\(1\leq x,y\leq n\)\(\gcd(x,y)\)为质数的数对\((x,y)\)个数
\(1\leq n\leq 10^7\)


欧拉\(\varphi\)函数的基础题,关于欧拉\(\varphi\)函数
所以基础知识就不在这篇里讲了

把题目描述成公式,并且枚举每一个质数\(p\)

\[\sum_{p\in {prime}}\sum_{x=1}^n\sum_{y=1}^n [\gcd(x,y)=p] \]

然后做一步变形,这好像是\(\gcd\)的常规操作

\[\sum_{p\in{prime}}\sum_{x=1}^{\lfloor\frac{n}{p}\rfloor}\sum_{y=1}^{\lfloor\frac{n}{p}\rfloor} [\gcd(x,y)=1] \]

上面这一步的意思大概就是,对于每一个\(\gcd(x,y)=p\)\((x,y)\),都能找到一个\(x',y',\gcd(x',y')=1\),使得\(\gcd(x'p,y'p)=p\)
那么,这些\((x',y')\)\((x,y)\)一一对应,所以数量一样多,于是求\((x,y)\)的个数就变成了求\((x',y')\)的个数
然后又因为\(x',y'\leq \lfloor\dfrac{n}{p}\rfloor\),所以只枚举到\(\lfloor\dfrac{n}{p}\rfloor\)就行了

再继续变形,发现\((x,y)\)是无序的,下面的\((x,y)\)就默认说的是\((x',y')\),所以可以通过改变\(y\)的上限变成有序然后再乘\(2\)来算

\[\sum_{p\in{prime}}(\sum_{x=1}^{\lfloor\frac{n}{p}\rfloor}(2\times \sum_{y=1}^x [\gcd(x,y)=1])-1) \]

要减\(1\)是因为\((x,x)\)是不符合要求的,多算了一个,看清楚上面式子里的括号

然后可以发现,最里面那个\(\sum\)就是\(\varphi\),所以可以变形成:

\[\sum_{p\in{prime}}((\sum_{x=1}^{\lfloor\frac{n}{p}\rfloor}2\varphi(x))-1) \]

\[\sum_{p\in{prime}}(2\sum_{x=1}^{\lfloor\frac{n}{p}\rfloor}\varphi(x)-1) \]

所以我们只需要线性筛求出\(\varphi\)再做个前缀和按公式算就行

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
	register int x=0;register int y=1;
	register char c=std::getchar();
	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
	return y?x:-x;
}
int n;
LL phi[10000006];
int prime[1000006],notprime[10000006];
inline void get_phi(){
	phi[1]=1;
	for(reg int i=2;i<=n;i++){
		if(!notprime[i]) prime[++prime[0]]=i,phi[i]=i-1;
		for(reg int j=1;j<=prime[0]&&i*prime[j]<=n;j++){
			notprime[i*prime[j]]=1;
			if(!(i%prime[j])){
				//i mod p=0, phi(i*p)=p*phi(i)
				phi[i*prime[j]]=prime[j]*phi[i];
				break;
			}
			else phi[i*prime[j]]=phi[i]*(prime[j]-1);//i mod p!=0,phi(i*p)=phi(i)*(p-1)
		}
	}
}
int main(){
	n=read();get_phi();
	for(reg int i=2;i<=n;i++) phi[i]+=phi[i-1];
	LL ans=0;
	for(reg int i=1;i<=prime[0];i++) ans+=2*phi[n/prime[i]]-1;
	std::printf("%lld",ans);
	return 0;
}
posted @ 2020-04-02 20:09  suxxsfe  阅读(121)  评论(0编辑  收藏  举报