[egin{aligned}
sumlimits_{i=1}^nsumlimits_{j=1}^m d(ij)&=sumlimits_{i=1}^nsumlimits_{j=1}^msumlimits_{x|i}sumlimits_{y|j} [(x,y)=1]\
&=sumlimits_{x=1}^nsumlimits_{y=1}^m[(x,y)=1]lfloor frac{n}{x}
floor lfloorfrac{m}{y}
floor\
&=sumlimits_{x=1}^nsumlimits_{y=1}^msumlimits_{d|x,y}mu(d)lfloor frac{n}{x}
floor lfloorfrac{m}{y}
floor\
&=sumlimits_{d=1}^nmu(d)sumlimits_{x=1}^{n/d}sumlimits_{y=1}^{m/d}lfloor frac{n}{xd}
floor lfloorfrac{m}{yd}
floor\
&=sumlimits_{d=1}^nmu(d)sumlimits_{x=1}^{n/d}d(x)sumlimits_{y=1}^{m/d}d(y)
end{aligned}
]
预处理函数 (d(x),mu(x)) 的前缀和然后整除分块即可。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define int long long
using namespace std;
const int N = 50000;
int n, m, u[N + 9], a[N + 9], p[N], cnt, d[N + 9];
void prework()
{
u[1] = d[1] = 1;
for (int i = 2; i <= N; i++)
{
if (!a[i])
a[i] = i, p[++cnt] = i, u[i] = -1, d[i] = 2;
for (int j = 1; j <= cnt; j++)
{
if (p[j] > a[i] || p[j] > N / i)
break;
a[p[j] * i] = p[j];
if (i % p[j] != 0)
u[p[j] * i] = u[p[j]] * u[i],
d[p[j] * i] = d[p[j]] * d[i];
else
d[p[j] * i] = 2 * d[i] - d[i / p[j]];
}
}
for (int i = 1; i <= N; i++)
d[i] += d[i - 1], u[i] += u[i - 1];
}
signed main()
{
prework();
int T;
scanf("%lld", &T);
while (T--)
{
scanf("%lld %lld", &n, &m);
if (n > m) swap(n, m);
int ans = 0;
for (int i = 1; i <= n; i++)
{
int k = min(n / (n / i), m / (m / i));
ans += d[n / i] * d[m / i] * (u[k] - u[i - 1]);
i = k;
}
printf("%lld
", ans);
}
}