[题解]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;
}
}