P1829 / SP8099 Crash的数字表格 题解

3 minute read

Published:

P1829 / SP8099 Crash的数字表格 题解

莫比乌斯反演、数论分块、杜教筛

题目传送门

题意简述

求以下式子的值,对 $20101009$(一个质数)取模:

\[\sum\limits_{i=1}^n \sum\limits_{j=1}^m \operatorname{lcm}(i,j)\]

$n,m \le 10^7$。加强:$n,m \le 10^{10}$。

解法

莫比乌斯反演

设 $s_1(n) = \sum\limits_{i=1}^n i = \dfrac {n (n+1)} 2$,$s_2(n) = \sum\limits_{i=1}^n i^2 = \dfrac {n (n+1) (2n+1)} 6$。

则答案可以通过莫比乌斯反演得到(这一段可以看其他题解),为:

\[\sum\limits_{T=1}^n s_1\left(\left\lfloor \dfrac n T \right\rfloor\right) \times s_1\left(\left\lfloor \dfrac m T \right\rfloor\right) \times T \times \sum\limits_{t|T} \mu(t)t\]

直接做可以 $O(n \log \log n)$ 或者 $O(n)$,但我们显然是不满意的。

杜教筛

两个下取整除法可以数论分块(瓶颈显然不在这),所以现在问题变为求 $f(T) = T \times \sum\limits_{tT} \mu(t)t$ 的前缀和。
\[f(T) = \sum\limits_{t|T} \mu(t)t^2 \times \dfrac T t\]

设函数 $\text{Id}(n) = n$,$\text{Id}_2(n) = n^2$。

\[f = (\mu \cdot \text{Id}_2) * \text{Id}\]

观察其贝尔级数:

\[f_p(x) = \dfrac{1 - p^2 x} {1 - px}\]

不难发现:

\[\dfrac{1 - p^2 x} {1 - px} \times \dfrac 1 {1 - p^2 x} = \dfrac 1 {1 - p x}\] \[f * \text{Id}_2 = \text{Id}\]

杜教筛即可。时间复杂度 $O(n^{\frac 2 3})$。

代码

代码是 Luogu P1829 次优解(截止至 2024/3/1),注意提交到 SP8099 需要将语言改为 C++98(当然 auto 之类的也要一起改掉)。

#include <bits/stdc++.h>
#define umap unordered_map
using namespace std;
typedef long long ll;

const ll MOD = 20101009;
const ll inv2 = 10050505;
const ll inv6 = 16750841;
const ll CBRT2 = 46415 + 100;
const ll MAXN2 = CBRT2 + 100;

ll s1(ll x) {
    return x * (x + 1) % MOD * inv2 % MOD;
}
ll s2(ll x) {
    return x * (x + 1) % MOD * (2 * x + 1) % MOD * inv6 % MOD;
}

ll n, m;

bool vis[MAXN2];
vector<ll> primes;
ll mu[MAXN2];
ll f[MAXN2], sum[MAXN2];
void sieve(ll n) {
    vis[1] = 1;
    mu[1] = 1;
    f[1] = 1;
    for (ll i = 2; i <= n; i++) {
        if (!vis[i]) {
            primes.push_back(i);
            mu[i] = -1;
            f[i] = i - i * i;
        }
        for (auto j : primes) {
            ll k = i * j; if (k > n) break;
            vis[k] = 1;
            if (i % j == 0) {
                mu[k] = 0;
                f[k] = f[i] * j;
                break;
            }
            mu[k] = mu[i] * mu[j];
            f[k] = f[i] * f[j];
        }
    }
    for (ll i = 1; i <= n; i++)
        sum[i] = (sum[i-1] + f[i]) % MOD;
}
umap<ll, ll> s;
ll S(ll x) {
    if (x <= CBRT2)
        return sum[x];
    if (s.count(x))
        return s[x];
    ll ans = s1(x);
    for (ll l = 2, r; l <= x; l = r + 1) {
        ll v = x / l;
        r = x / v;
        ans -= (s2(r) - s2(l-1)) * S(v) % MOD;
    }
    (ans += MOD) %= MOD;
    return s[x] = ans;
}
ll solve(ll n, ll m) {
    ll ans = 0;
    for (ll l = 1, r; l <= n; l = r + 1) {
        ll v1 = n / l, v2 = m / l;
        r = min(n / v1, m / v2);
        (ans += s1(v1) * s1(v2) % MOD * (S(r) - S(l-1) + MOD) % MOD) %= MOD;
    }
    return ans;
}

int main() {
    cin >> n >> m;
    if (n > m) swap(n, m);
    sieve(min(n, CBRT2));
    cout << solve(n, m) << '\n';
    return 0;
}