∵φ*1=id∴h=φ
解了这么久再带入原式
$$\sum_{x=1}^n \sum_{y=1}^m \gcd(x,y)$$
$$=\sum_{T=1}^{\min(n,m)}\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor \varphi(T)$$
然后就很套路了。和$\text{ZAP Queries}$一样,预处理出欧拉函数前缀和,然后用整除分块,这个题目就$O(\min(n,m))$地解决了。
欧拉函数前缀和用欧拉筛求(什么鬼)
## 后记
信息数论和普通数论的区别就在于:它不一定是个结论题。你在推导的过程中,需要考虑——什么时候要停笔去枚举。
这也就是信息数论的难点所在。
```
#include<bits/stdc++.h>
using namespace std;
int tot,T,vis[100004];
long long phi[100004],sum[100004],p[100004],n,m;
inline void init(){//筛欧拉函数
int nx=100000;phi[1]=1;
for (register int i=2;i<=nx;i++){
if (!vis[i]) p[++tot]=i,phi[i]=i-1;
for (register int j=1;j<=tot&&i*p[j]<=nx;j++){
if (i*p[j]>nx) continue;
vis[i*p[j]]=1;
phi[i*p[j]]=phi[i]*phi[p[j]];
if((i%p[j])==0){
phi[i*p[j]]=phi[i]*p[j];
break;
}
}
}
for (int i=1;i<=nx;i++){
sum[i]=sum[i-1]+phi[i];//求前缀和
}
}
int main(){
scanf("%lld%lld",&n,&m);
init();
long long ans=0;
for (int l=1,r;l<=min(n,m);l=r+1){
r=min(n/(n/l),m/(m/l));
ans+=(long long)(sum[r]-sum[l-1])*(n/l)*(m/l);//整除分块
}
ans=(long long)2*ans-n*m;
printf("%lld",ans);
}
```