题解 P2257 【YY的GCD】
神犇 YY 虐完数论后给傻× kAc 出了一题
给定 \(N, M\) 求 \(1 \leq x \leq N\),\(1 \leq y \leq M\) 且 \(\gcd(x, y)\) 为质数的 \((x, y)\) 有多少对。
前置知识
\[\begin{aligned}
&\sum_{p\in prim}\sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)=p] \\
=& \sum_{p\in prim}\sum_{i=1}^{\lfloor\frac{n}{p}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{p}\rfloor}[\gcd(i,j)=1]\\
=& \sum_{p\in prim}\sum_{i=1}^{\lfloor\frac{n}{p}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{p}\rfloor}\varepsilon(\gcd(i,j)=1)\\
=& \sum_{p\in prim}\sum_{i=1}^{\lfloor\frac{n}{p}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{p}\rfloor}\varepsilon(\gcd(i,j))\\
=& \sum_{p\in prim}\sum_{i=1}^{\lfloor\frac{n}{p}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{p}\rfloor} \sum_{d\mid \gcd(i,j)} \mu(d)\\
=&\sum_{p\in prim}\sum_{p\mid d} \mu(d)\sum_{i=1}^{\lfloor\frac{n}{p}\rfloor}[d\mid i]\sum_{j=1}^{\lfloor\frac{m}{p}\rfloor}[d\mid j]\\
=&\sum_{p\in prim}\sum_{p\mid d} \mu(d) \lfloor \frac{n}{kd} \rfloor\lfloor \frac{m}{kd} \rfloor\\
\end{aligned}
\]
设:
\[f(p)=\sum\limits_{p\mid d} \mu(d)
\]
则原式化为:
\[\begin{aligned}
=&\sum_{p\in prim} f(p)\times \lfloor \frac{n}{kd} \rfloor\lfloor \frac{m}{kd} \rfloor\\\end{aligned}
\]
我们先去预处理 \(f\) 函数
for(int i=1;i<=cnt;i++)
for(int j=1;j*prim[i]<N;j++)
f[j*prim[i]]+=mu[j];
就是这里的 \(f\) 数组。
之后我们就可以求解了。
我们可以使用数论分块来写这题。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
T RR=1;FF=0;char CH=getchar();
for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
FF*=RR;
}
const int N=1e7+10;
int prim[N],mu[N],sum[N],cnt,k,T,f[N];
bool vis[N];
void init(){
mu[1]=1;
for(register int i=2;i<N;i++){
if(!vis[i]){
mu[i]=-1;
prim[++cnt]=i;
}
for(register int j=1;j<=cnt&&i*prim[j]<N;j++){
vis[i*prim[j]]=1;
if(i%prim[j]==0)break;
mu[i*prim[j]]=-mu[i];
}
}
for(int i=1;i<=cnt;i++)
for(int j=1;j*prim[i]<N;j++)
f[j*prim[i]]+=mu[j];
for(register int i=1;i<N;i++)sum[i]=sum[i-1]+f[i];
}//莫比乌斯反演的板子
ll calc(int a,int b){
ll ans=0;
for(register int l=1,r;l<=min(a,b);l=r+1){
r=min(a/(a/l),b/(b/l));
ans+=(1ll*a/l)*(1ll*b/l)*(sum[r]-sum[l-1]);
}
return ans;
}
int main(){
init();
for(read(T);T--;){
int x,y;
read(x);read(y);
printf("%lld\n",calc(x,y));
}
return 0;
}