【洛谷P2257】YY的GCD
题目
题目链接:https://www.luogu.com.cn/problem/P2257
给定 \(n,m\),求 \(\gcd(a,b)\) 为质数且 \(1\leq a\leq n,1\leq b\leq m\) 的 \((a,b)\) 有多少对。
思路
经典的莫比乌斯反演模板题。
设 \(f(i)\) 表示 \(\gcd(a,b)=i\) 的方案数,设 \(F(i)\) 表示 \(\gcd(a,b)\) 为 \(i\) 的倍数的方案数。其中 \(i\) 为质数。
那么有
\[F(i)=\left \lfloor \frac{n}{i} \right \rfloor\left \lfloor \frac{m}{i} \right \rfloor
\]
\[f(i)=\sum^{\min(n,m)}_{i|d}\mu(\frac{d}{i})F(i)=\sum^{\min(n,m)}_{i|d}\mu(\frac{d}{i})\left \lfloor \frac{n}{d} \right \rfloor\left \lfloor \frac{m}{d} \right \rfloor
\]
那么
\[ans=\sum^{min(n,m)}_{p\in \operatorname{prm}}\sum^{\min(n,m)}_{p|d}\mu(\frac{d}{p})\left \lfloor \frac{n}{d} \right \rfloor\left \lfloor \frac{m}{d} \right \rfloor
\]
\[=\sum^{min(n,m)}_{d=1}\left ( \sum^{\min(n,m)}_{p|d,p\in\operatorname{prm}} \mu(\frac{d}{p})\right )\left \lfloor \frac{n}{d} \right \rfloor\left \lfloor \frac{m}{d} \right \rfloor
\]
那么直接线性筛出 \(\mu\),然后利用埃氏筛思想与处理出 \(\sum^{\min(n,m)}_{p|d,p\in\operatorname{prm}} \mu(\frac{d}{p})\) 以及其前缀和。
对于每一次询问,对 \(\left \lfloor \frac{n}{d} \right \rfloor\left \lfloor \frac{m}{d} \right \rfloor\) 整除分块,然后乘上相应前缀和即可。
时间复杂度 \(O(n\log \log n+T\sqrt{n})\)。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=10000010;
int Q,cnt,n,m,prm[N],mu[N],sum[N];
ll ans;
bool v[N];
void findprm(int n)
{
mu[1]=1;
for (int i=2;i<=n;i++)
{
if (!v[i])
{
prm[++cnt]=i;
mu[i]=-1;
}
for (int j=1;j<=cnt;j++)
{
if (i>n/prm[j]) break;
v[prm[j]*i]=1; mu[prm[j]*i]=-mu[i];
if (!(i%prm[j]))
{
mu[prm[j]*i]=0;
break;
}
}
}
}
int main()
{
findprm(N-10);
for (int i=1;i<=cnt;i++)
for (int j=prm[i];j<N;j+=prm[i])
sum[j]+=mu[j/prm[i]];
for (int i=1;i<N;i++)
sum[i]+=sum[i-1];
scanf("%d",&Q);
while (Q--)
{
scanf("%d%d",&n,&m);
ans=0;
for (int l=1,r;l<=min(n,m);l=r+1)
{
int d1=n/l,d2=m/l;
r=min(n/d1,m/d2);
ans+=1LL*(sum[r]-sum[l-1])*d1*d2;
}
printf("%lld\n",ans);
}
return 0;
}