第一道自己推出来的莫比乌斯反演题。
下文默认 \(n\le m\)
下文 \(\varepsilon(x)=[x=1]=\sum_{d|x}\mu(d)\)
题意:求
可以在度娘上找到
带入式子
更换枚举顺序,先枚举 \(i,j\),则前面枚举的 \(x,y\) 需满足 \(i|x,j|y\)
可以发现 \(\sum^n_{i|x}\sum^m_{j|y}\),这两个式子就是分别求 \(1\) ~ \(n\) 中 \(i\) 的倍数的个数和 $1 $ ~ \(m\) 中 \(j\) 的倍数的个数,这两个和式等价于 \(n/i,m/j\),所以
套路的把 \([\gcd(i,j)=1]\) 换成 \(\varepsilon(\gcd(i,j))\),再把它转成 \(\sum_{d|\gcd(i,j)}\mu(d)\) 的形式
改变枚举顺序,先枚举 \(d\),并把 \(\mu(d)\) 提到式子前面,因为 \(d|\gcd(i,j)\),必有 \(d|i ,d|j\)
把后面两个和式改成枚举 \(di,dj\) 的形式,则上标都除以 \(d\),\(di,dj\) 此时都是 \(d\) 的倍数,后面含有 \(i,j\) 的向下取整的都要变成 \(di,dj\)
把其中一个向下取整移出来
这下两边都是数论分块可以求出的了。
预处理出 \(g(n)=\sum_{i=1}^{n} \left\lfloor \frac{n}{i}\right \rfloor\),则两个和式变成 \(g(\left \lfloor \frac{n}{d}\right \rfloor),g(\left \lfloor \frac{m}{d}\right \rfloor)\),所以
这个式子也用数论分块做即可,后面因为预处理了,可以 \(O(1)\) 做。
总复杂度 \(O(T\sqrt n)\)
\(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=50005;
int p[MAX],tot=0,f[MAX],mu[MAX];
inline void init(){
mu[1]=1;
for(register int i=2;i<=MAX;++i){
if(!f[i])p[++tot]=i,mu[i]=-1;
for(register int j=1;j<=tot&&i*p[j]<=MAX;++j){
f[i*p[j]]=1;
if(i%p[j]==0)break;
mu[i*p[j]]=-mu[i];
}
}
memset(f,0,sizeof(f));
for(register int i=1;i<=MAX;++i){
mu[i]+=mu[i-1];
for(register int l=1,r;l<=i;l=r+1){
r=i/(i/l);
f[i]+=1LL*(r-l+1)*(i/l);
}
}
}
int ans,T;
inline int solve(int n,int m){
ans=0;
for(register int l=1,r;l<=n;l=r+1){
r=min(n/(n/l),m/(m/l));
ans+=1LL*(mu[r]-mu[l-1])*f[n/l]*f[m/l];
}
return ans;
}
int n,m;
signed main(){
init();
T=read();
while(T--){
n=read(),m=read();
if(n>m)n^=m^=n^=m;
printf("%lld\n",solve(n,m));
}
return 0;
}