洛谷P3455 [POI2007]ZAP-Queries【莫比乌斯反演】
题解
这算是莫比乌斯反演的最简模板了吧。
首先手推公式,设:
\[f(d)=\sum_{x=1}^{N}\sum_{y=1}^{M}[gcd(x,y)=d]\qquad (N\le M)
\]
根据 \(F(n)=\sum_{n|d}f(d)\),得:
\[F(n)=\bigg\lfloor\frac{N}{n}\bigg\rfloor\bigg\lfloor\frac{M}{n}\bigg\rfloor
\]
根据莫比乌斯反演,得:
\[f(n)=\sum_{n|d}\mu(\frac{d}{n})F(d)=\sum_{n|d}\mu(\frac{d}{n})\bigg\lfloor\frac{N}{d}\bigg\rfloor\bigg\lfloor\frac{M}{d}\bigg\rfloor
\]
枚举 \(t=\frac{d}{n}\),得:
\[f(n)=\sum_{t=1}^{\big\lfloor\frac{N}{n}\big\rfloor}\mu(t)\bigg\lfloor\frac{N}{nt}\bigg\rfloor\bigg\lfloor\frac{M}{nt}\bigg\rfloor
\]
剩下的工作就要编程实现了,利用对 \(\mu\) 求前缀和与整除分块,就可以在 \(O(\sqrt{n})\) 以内回答每个问题了。
感觉莫比乌斯反演最难的还是通过 \(f(d)\) 正确得到 \(F(n)\)。
代码
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
using namespace std;
typedef long long LL;
const int N=5e4+10;
int n,m,vis[N],prime[N],cnt,mu[N],sum[N],d;
void init(int n){
mu[1]=1;
for(int i=2;i<=n;i++){
if(!vis[i]) {prime[++cnt]=i;mu[i]=-1;}
for(int j=1;j<=cnt&&1ll*i*prime[j]<=n;j++){
vis[i*prime[j]]=1;
if(i%prime[j]==0) {mu[i*prime[j]]=0;break;}
mu[i*prime[j]]=-mu[i];
}
}
for(int i=1;i<=n;i++) sum[i]=sum[i-1]+mu[i];
}
int main(){
init(5e4);
int T;scanf("%d",&T);
while(T--){
LL ans=0;
scanf("%d%d%d",&n,&m,&d);
n/=d,m/=d;
for(int l=1,r;l<=min(n,m);l=r+1){
r=min(n/(n/l),m/(m/l));
if(r>min(n,m)) break;
ans+=1ll*(sum[r]-sum[l-1])*(n/l)*(m/l);
}
printf("%lld\n",ans);
}
return 0;
}