题目链接:Click here
Solution:
首先,我们转化式子
[sum_{i=1}^nsum_{j=1}^m d(ij)\
sum_{i=1}^nsum_{j=1}^m sum_{x|i} sum_{y|j}[gcd(x,y)=1]\
]
我们把(x,y)给提前
[sum_{x=1}^nsum_{y=1}^m lfloor frac{n}{x}
floor lfloor frac{m}{y}
floor [gcd(x,y)=1]
]
我们把(gcd(x,y))提前,(x,y)不太好看,再给他换个名字(i,j)
[sum_{i=1}^n sum_{j=1}^m lfloor frac{n}{i}
floor lfloor frac{m}{j}
floor sum_{d|i} sum_{d|j} mu(d)\
]
我们把(d)提前
[sum_{d=1}^n mu(d) sum_{i=1}^{lfloor frac{n}{d}
floor} sum_{j=1}^{lfloor frac{m}{d}
floor} lfloor frac{n}{di}
floor lfloor frac{m}{dj}
floor
]
我们设一个函数(g(n)=sum_{i=1}^n lfloor frac{n}{i} floor),则有
[sum_{d=1}^n mu(d) g(lfloor frac{n}{d}
floor) g(lfloor frac{m}{d}
floor)
]
这个数论分块即可,考虑怎么筛(g(n)),我们可以发现(g(n)=sum_{i=1}^n d(i)),则我们筛(d)后做前缀和即可
Code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e4+11;
int n,m,u[N],p[N],vis[N];
int ans,cnt,g[N],num[N];
int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
void prepare(){
u[1]=1;g[1]=1;
for(int i=2;i<N;i++){
if(!vis[i]) p[++cnt]=i,u[i]=-1,g[i]=2,num[i]=1;
for(int j=1;j<=cnt&&i*p[j]<N;j++){
vis[i*p[j]]=1;
if(i%p[j]==0){
u[i*p[j]]=0;
g[i*p[j]]=g[i]/(num[i]+1)*(num[i]+2);
num[i*p[j]]=num[i]+1;
break;
}
u[i*p[j]]=-u[i];
g[i*p[j]]=g[i]*2;
num[i*p[j]]=1;
}
}
for(int i=1;i<N;i++)
u[i]=u[i]+u[i-1],g[i]=g[i]+g[i-1];
}
void solve(){
n=read(),m=read();
ans=0;
for(int i=1,j;i<=min(n,m);i=j+1){
j=min(n/(n/i),m/(m/i));
ans=ans+(u[j]-u[i-1])*g[n/i]*g[m/i];
}
printf("%lld
",ans);
}
signed main(){
prepare();
int t=read();
while(t--) solve();
return 0;
}