[题解]YY的GCD【Luogu P2257】

YY的GCD

一.提要

​ 这片题解是一个大的学习笔记中的附属品,其中没有什么变换讲解,如果看不懂请先一步到这篇文章

二.解题

\[\sum\limits_{p\in P} \sum\limits_{x=1}^{n} \sum\limits_{y=1}^m[gcd(x,y)=p] \]

\[=\sum\limits_{p\in P} \sum\limits_{x=1}^{\lfloor\frac{n}{p}\rfloor} \sum\limits_{y=1}^{\lfloor\frac{m}{p}\rfloor} \sum\limits_{d|\gcd(x,y)}\mu(d)\\ \]

\[=\sum\limits_{p\in P} \sum\limits_{d=1}^{\lfloor\frac{min(n,m)}{p}\rfloor} \mu(d)\lfloor\frac{n}{pd}\rfloor \lfloor\frac{m}{pd}\rfloor\\ \]

\[=\sum\limits_{t=1}^{min(n,m)} \lfloor\frac{n}{t}\rfloor \lfloor\frac{m}{t}\rfloor \sum\limits_{p\in P,p|t}\mu(\frac{t}{p}) \]

第二步运用了\(\mu*1=\epsilon\)

第三步运用了一个……没有名字的定理来划掉了两个求和(在程序实现的时候需要数论分块解决\(\lfloor\frac{n}{pd}\rfloor \lfloor\frac{m}{pd}\rfloor\)的不同取值)

第四步换元,调换顺序

三.CODE

#define ll long long
#define m_for(i,a,b) for(int i=a;i<=b;++i)
inline int read() {
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9') {
		if(ch=='-')w=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
	return s*w;
}
const int MAXN=10000005;
int prime[MAXN],tot,mu[MAXN];
bool vis[MAXN];
void m_p(int x){
	mu[1]=1;
	m_for(i,2,x){
		if(!vis[i])prime[++tot]=i,mu[i]=-1;
		for(int j=1;i*prime[j]<=x&&j<=tot;++j){
			vis[i*prime[j]]=1;
			if(i%prime[j]==0)break;
			mu[i*prime[j]]=-mu[i];
		}
	}
}
int g[MAXN];
ll sum[MAXN];
void m_mu(int x){
	m_for(i,1,tot){
		for(int j=1;j*prime[i]<=x;++j){
			g[j*prime[i]]+=mu[j];
		}
	}
	m_for(i,1,x)sum[i]=sum[i-1]+1LL*g[i];
}
int t,n,m;
ll ans;
int main(){
	m_p(MAXN);
	m_mu(MAXN);
	t=read();
	while(t--){
		n=read();m=read();
		if(n>m)swap(n,m);
		ans=0;
		for(int l=1,r;l<=n;l=r+1){
			r=min(n/(n/l),min(m/(m/l),n));
			ans+=1LL*(n/l)*(m/l)*(sum[r]-sum[l-1]);
			//cout<<ans<<endl;
		}
		cout<<ans<<endl;
	}
}
posted @ 2020-02-02 09:11  clockwhite  阅读(110)  评论(0编辑  收藏  举报