此题为套路题
前置芝士
数论分块,莫比乌斯反演
做法
首先可以看出题目求
\[\sum_{i=1}^{a} \sum_{j=1}^{b}[gcd(i,j)=d] \]
首先很套路地转化为
\[\sum_{i=1}^{\left \lfloor \frac{a}{d} \right \rfloor}\sum_{i=1}^{\left \lfloor \frac{b}{d} \right \rfloor}[gcd(i,j)=1] \]
有一个数论函数 \(\varepsilon(x)\),当\(x=1\)时,函数值为\(1\)否则为\(0\)
后面的\([gcd(i,j)=1]\)可以用\(\varepsilon(gcd(i,j))\)
\[\sum_{i=1}^{\left \lfloor \frac{a}{d} \right \rfloor}\sum_{i=1}^{\left \lfloor \frac{b}{d} \right \rfloor}\varepsilon(gcd(i,j)) \]
用 \(\mu*1 = \varepsilon\) 反演
\[\sum_{i=1}^{\left \lfloor \frac{a}{d} \right \rfloor}\sum_{i=1}^{\left \lfloor \frac{b}{d} \right \rfloor}\sum_{p|gcd(i,j)}\mu(p) \]
改变求和顺序
\[\sum_{p=1}\mu(p)\sum_{i=1}^{\left \lfloor \frac{a}{d} \right\rfloor}[p \ | \ i]\sum_{j=1}^{\left \lfloor \frac{b}{d} \right \rfloor}[p \ |\ j] \]
后面两个\(\sum\),可以看出求的是\(d\)有多少个倍数在\(\left \lfloor \frac{a}{d} \right\rfloor\)和\(\left \lfloor \frac{b}{d} \right \rfloor\)中。显然,答案分别为\(\left \lfloor \frac{a}{pd} \right\rfloor\)和
\(\left \lfloor \frac{b}{pd} \right\rfloor\)
转化为
\[\sum_{p=1}\mu(p)\left \lfloor \frac{a}{pd} \right\rfloor\left \lfloor \frac{b}{pd} \right\rfloor \]
然后数论分块加前缀和就做完了
我Code里的是先把\(a/d,b/d\),再做数论分块,这样常数会小一点。
Code
#include
#include
#include
using namespace std;
#define int long long
inline int read(){
register int x=0,f=0,ch=getchar();
while('0'>ch||ch>'9')f^=ch=='-',ch=getchar();
while('0'<=ch&&ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=getchar();
return f?-x:x;
}
const int MAX=50100;
int mu[MAX],pri[MAX],f[MAX],tot;
inline void Getmu(int N){
mu[1]=1;
for(register int i=2;i<=N;++i){
if(!f[i]){
mu[i]=-1;
pri[++tot]=i;
}
for(register int j=1;j<=tot&&i*pri[j]<=N;++j){
f[i*pri[j]]=1;
if(i%pri[j]==0)break;
mu[i*pri[j]]=-mu[i];
}
}
for(register int i=1;i<=N;++i)mu[i]+=mu[i-1];
}
int ans;
inline int solve(int n,int m){
ans=0;
if(n>m)n^=m^=n^=m;
for(register int l=1,r;l<=n;l=r+1){
r=min(n/(n/l),m/(m/l));
ans+=(mu[r]-mu[l-1])*(n/l)*(m/l);
}
return ans;
}
int t,a,b,d;
signed main(){
Getmu(50000);
t=read();
while(t--){
a=read(),b=read(),d=read();
a/=d,b/=d;
printf("%lld\n",solve(a,b));
}
return 0;
}