(sum_{i=1}^{n}sum_{j=1}^{m} lcm(i,j) = frac{i,j}{gcd(i,j)})
枚举 (gcd)
(sum_{d=1}^{n}sum_{i=1}^{n/d}sum_{j=1}^{m/d}ij imes d[gcd(i,j)==1])
(sum_{i|n} mu(i) = [n==1])
所以
(sum_{d=1}^{n}sum_{i=1}^{n/d}sum_{j=1}^{m/d}ij imes d sum_{k|gcd(i,j)} mu(k))
枚举 (k)!
因为 (k|gcd(i,j)),所以 (k|i,k|j)
(sum_{d=1}^{n}sum_{k=1}^{n}mu(k)sum_{i=1}^{n/kd}sum_{j=1}^{m/kd}ikjk imes d)
发现 (sum_{i=1}^{x}sum_{j=1}^{y}ij = sum2(x) imes sum2(y))
此处定义 (sum2(x) = frac{(1+x) imes x}{2})
然后原式就相当于
(sum_{d=1}^{n}dsum_{k=1}^{n}k^2mu(k)sum2(n/kd) imes sum2(m/kd))
发现 (d imes k leq n)!
枚举 (d imes k)!
然后狄利克雷前缀和一下就没了。
// by Isaunoya
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
const int mod = 20101009;
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
auto add = [&](int&a, const int&b) {
a += b;
if(a >= mod)
a -= mod;
};
auto init = [&](int n) {
vector <int> mu(n + 1, 0);
for(int i = mu[1] = 1; i <= n; i++)
for(int j = i * 2; j <= n; j += i)
mu[j] -= mu[i];
vector <bool> vis(n + 1, 0);
vector <int> prime;
for(int i = 2; i <= n; i++) {
if(vis[i])
continue;
prime.pb(i);
for(int j = i * 2; j <= n; j += i)
vis[j] = 1;
}
vector <int> sum(n + 1, 0);
for(int i = 1; i <= n; i++)
sum[i] = (mu[i] * i + mod) % mod;
for(int p: prime)
for(int j = 1; j * p <= n; j++)
add(sum[p * j], sum[j]);
return sum;
};
int n, m;
cin >> n >> m;
if(n > m)
swap(n, m);
vector <int> s = init(n);
auto get = [&](const int&x) {
int res = 1ll * x * s[x] % mod;
auto sum = [&](const int&x) { return 1ll * x * (x + 1) / 2 % mod; };
res = 1ll * res * sum(n / x) % mod;
res = 1ll * res * sum(m / x) % mod;
return res;
};
int ans = 0;
for(int i = 1; i <= n; i ++)
add(ans, get(i));
cout << ans << '
';
return 0;
}