P1829 [国家集训队]Crash的数字表格 / JZPTAB [莫比乌斯反演,狄利克雷前缀和]
\(\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\times 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\times 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\times d\)
发现 \(\sum_{i=1}^{x}\sum_{j=1}^{y}ij = sum2(x) \times sum2(y)\)
此处定义 \(sum2(x) = \frac{(1+x)\times x}{2}\)
然后原式就相当于
\(\sum_{d=1}^{n}d\sum_{k=1}^{n}k^2\mu(k)sum2(n/kd)\times sum2(m/kd)\)
发现 \(d\times k \leq n\)!
枚举 \(d\times 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 << '\n';
return 0;
}