[国家集训队]Crash的数字表格 / JZPTAB 题解
知识点:
Dirichlet 前缀和 + 线性筛。
推导:
默认 \(n \leq m\)
\(ans = \displaystyle \sum_{i = 1}^n \sum_{j = 1}^{m} lcm(i,j) \\ = \displaystyle \sum_{d = 1}^{n} d \sum_{i = 1}^{\lfloor \frac{n}{d} \rfloor} \sum_{j = 1}^{\lfloor \frac{m}{d} \rfloor} ij \\ = \displaystyle \sum_{d = 1}^{n} d \sum_{i = 1}^{\lfloor \frac{n}{d} \rfloor} \sum_{j = 1}^{\lfloor \frac{m}{d} \rfloor} ij \\ = \displaystyle \sum_{d = 1}^{n} d \sum_{k = 1}^{\lfloor \frac{n}{d} \rfloor} \mu(k)k^2 \sum_{i = 1}^{\lfloor \frac{n}{kd} \rfloor} \sum_{j = 1}^{\lfloor \frac{m}{kd} \rfloor} ij\)
下面不妨令 \(T = kd\)。可以得到:
\(ans = \displaystyle \sum_{T = 1}^n T \sum_{d|T} \mu(d)d \sum_{i = 1}^{\lfloor \frac{n}{T} \rfloor} \sum_{j = 1}^{\lfloor \frac{m}{T} \rfloor} ij\)
不妨令 \(g(n, T)\) = \(\displaystyle \sum_{i = 1}^{\lfloor \frac{n}{T} \rfloor}\),可以很容易得到 \(g(T) = (\frac{n}{T} + 1) * \frac{n}{T} / 2\)
那么
\(ans = \displaystyle \sum_{T = 1}^n (T \sum_{d|T} \mu(d)d) \times g(n, T) \times g(m, T)\)
前面的 \(\displaystyle \sum_{d|T} \mu(d)d\) 用 Dirichlet 前缀和预处理即可在 \(O(n \log \log n)\) 的复杂度内完成问题的预处理。
对于询问用整除分块优化即可做到 \(\sqrt{n}\) 处理。
Code
#include <bits/stdc++.h>
using namespace std;
const int Mod = 20101009, MAXN = 1e7 + 5, inv = (Mod + 1) >> 1;
int n, m;
int Prime[MAXN / 11], Mu[MAXN], tot = 0, S[MAXN];
bool bk[MAXN];
void GetPrime(int N) {
Mu[1] = 1;
for(int i = 2 ; i <= N ; i ++) {
if(!bk[i]) Prime[++ tot] = i, Mu[i] = -1;
for(int j = 1 ; j <= tot && Prime[j] * i <= N; j ++) {
bk[Prime[j] * i] = 1;
if(i % Prime[j] == 0) break;
Mu[Prime[j] * i] = - Mu[i];
}
}
for(int i = 1 ; i <= N ; i ++) S[i] = 1ll * ((Mu[i] + Mod) % Mod) * i % Mod;
return ;
}
int Sum(int x) { return 1ll * x * (x + 1) % Mod * inv % Mod; }
int main() {
cin >> n >> m;
if(n > m) swap(n, m);
GetPrime(n);
for(int i = 1 ; i <= tot ; i ++)
for(int j = 1 ; j * Prime[i] <= n ; j ++)
S[Prime[i] * j] += S[j], S[Prime[i] * j] %= Mod;
int Ans = 0;
for(int i = 1 ; i <= n ; i ++) {
Ans += 1ll * i * S[i] % Mod * Sum(n / i) % Mod * Sum(m / i) % Mod;
Ans %= Mod;
}
cout << Ans;
return 0;
}