题解
这算是莫比乌斯反演的最简模板了吧。
首先手推公式,设:
[f(d)=sum_{x=1}^{N}sum_{y=1}^{M}[gcd(x,y)=d]qquad (Nle M)
]
根据 (F(n)=sum_{n|d}f(d)),得:
[F(n)=igglfloorfrac{N}{n}igg
floorigglfloorfrac{M}{n}igg
floor
]
根据莫比乌斯反演,得:
[f(n)=sum_{n|d}mu(frac{d}{n})F(d)=sum_{n|d}mu(frac{d}{n})igglfloorfrac{N}{d}igg
floorigglfloorfrac{M}{d}igg
floor
]
枚举 (t=frac{d}{n}),得:
[f(n)=sum_{t=1}^{iglfloorfrac{N}{n}ig
floor}mu(t)igglfloorfrac{N}{nt}igg
floorigglfloorfrac{M}{nt}igg
floor
]
剩下的工作就要编程实现了,利用对 (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
",ans);
}
return 0;
}