P2257 YY的GCD(莫比乌斯反演)

时间:2023-03-08 21:05:01

第一次做莫比乌斯反演,推式子真是快乐的很啊(棒读)

前置

若函数\(F(n)\)和\(f(d)\)存在以下关系

\[ F(n)=\sum_{n|d}f(d)
\]

则可以推出

\[ f(n)=\sum_{n|d}\mu(\frac{d}{n})F(d)
\]

这就是莫比乌斯反演

题目要求

求\(gcd(a,b)=\{prime\},a\in\left[1,n\right],b\in\left[1,m\right]\)

思路

根据题意所以设出\(f(n)\)表示\(gcd(a,b)=n\)的\(a,b\)对数

根据莫比乌斯反演的形式

\[ F(n)=\sum_{n|d}f(d)
\]

可以设出一个函数\(F(n)\),表示\(n|gcd(a,b)\)的\((a,b)\)对数

因为\(n|gcd(a,b)\),所以\(a=k_1\times n,b=k_2\times n\)

所以显然有

\[ F(x)=\lfloor\frac{n}{x}\rfloor\times\lfloor\frac{m}{x}\rfloor
\]

因为

\[ f(x)=\sum_{x|d}\mu(\frac{d}{x})F(d)
\]

所以

\[ f(x)=\sum_{x|d}\mu(\frac{d}{x})\times\lfloor\frac{n}{d}\rfloor\times\lfloor\frac{m}{d}\rfloor
\]

考虑到\(\frac{d}{n}\)的形式并不优美,我们换一种东西枚举

\[ f(x)=\sum_{t=1}^{\lfloor\frac{n}{x}\rfloor}\mu(t)\times\lfloor\frac{n}{t\times x}\rfloor\times\lfloor\frac{m}{t\times x}\rfloor
\]

所以

\[ ans=\sum_{x\in\{prime\}}^nf(x)=\sum_{x\in\{prime\}}^n\sum_{t=1}^{\lfloor\frac{n}{x}\rfloor}\mu(t)\times\lfloor\frac{n}{t\times x}\rfloor\times\lfloor\frac{m}{t\times x}\rfloor
\]

这样能拿到50PTS

然后设\(T=t*x\),这样形式就变得更优美了一些

原式变形为

\[ ans=\sum_{x\in\{prime\}}^nf(x)=\sum_{x\in\{prime\}}^n\sum_{t=1}^{\lfloor\frac{n}{x}\rfloor}\mu(\frac{T}{x})\times\lfloor\frac{n}{T}\rfloor\times\lfloor\frac{m}{T}\rfloor
\]

\[ ans=\sum_{x\in\{prime\}}^nf(x)=\sum_{T=1}^{min(n,m)}\sum_{d\in\{prime\},d|T}\mu(\frac{T}{d})\times\lfloor\frac{n}{T}\rfloor\times\lfloor\frac{m}{T}\rfloor
\]

\[ ans=\sum_{x\in\{prime\}}^nf(x)=\sum_{T=1}^{min(n,m)}\lfloor\frac{n}{T}\rfloor\times\lfloor\frac{m}{T}\rfloor\times\sum_{d\in\{prime\},d|T}\mu(\frac{T}{d})
\]

后面\(\mu\)的部分可以前缀和一下

前面的可以整除分块

加上线性筛

然后没了

代码

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
short mu[10001000];
int n,m,T,iprime[10001000],cnt,isprime[10001000],summu[10001000];
void prime(int n){
mu[1]=1;
isprime[1]=true;
for(int i=2;i<=n;i++){
if(!isprime[i])
iprime[++cnt]=i,mu[i]=-1;
for(int j=1;j<=cnt&&iprime[j]*i<=n;j++){
isprime[iprime[j]*i]=true;
if(i%iprime[j]==0){
mu[iprime[j]*i]=0;
break;
}
mu[iprime[j]*i]=-mu[i];
}
}
for(int i=1;i<=cnt;i++)
for(int j=1;j*iprime[i]<=n;j++)
summu[iprime[i]*j]+=mu[j];
for(int i=1;i<=n;i++)
summu[i]+=summu[i-1];
}
long long f(int n,int m){
long long ans=0;
for(int l=1,r;l<=min(n,m);l=r+1){
r=min(n/(n/l),m/(m/l));
ans+=1LL*(summu[r]-summu[l-1])*(n/l)*(m/l);
}
return ans;
}
int main(){
prime(10000100);
scanf("%d",&T);
while(T--){
long long ans=0;
scanf("%d %d",&n,&m);
if(n<m)
swap(n,m);
ans+=f(n,m);
printf("%lld\n",ans);
}
return 0;
}