「LOJ 6229」这是一道简单的数学题

题目链接:LOJ 6229

这是一道非常简单的数学题。

最近 LzyRapx​ 正在看 mathematics for computer science 这本书,在看到数论那一章的时候,LzyRapx 突然想到这样一个问题。

$$ F(n) = \sum_{i = 1} ^ n \sum_{j = 1} ^ i \frac{\operatorname{lcm}(i, j)}{\gcd(i, j)} $$

其中,$\operatorname{lcm}(a, b)$ 表示 $a$ 和 $b$ 的最小公倍数,$\gcd(a, b)$ 表示 $a$ 和 $b​$ 的最大公约数。

给定 $n$ ,让你求: $F(n) \bmod (10 ^ 9 + 7)​$。

数据范围:$1 \le n \le 10 ^ 9$。


Solution

由于 $j$ 的上界为 $i$,处理起来比较麻烦,我们将其上界强行转化为 $n$,那么最终的答案为 $\frac{F(n) + n}{2}$。

接下来就是愉快的推式子时间啦!

$$ \begin{align*} F(n) & = \sum_{i = 1} ^ n \sum_{j = 1} ^ n \frac{\operatorname{lcm}(i, j)}{\gcd(i, j)} \\ & = \sum_{i = 1} ^ n \sum_{j = 1} ^ n \frac{i \cdot j}{\gcd(i, j) ^ 2} \\ & = \sum_{d = 1} ^ n \sum_{i = 1} ^ n \sum_{j = 1} ^ n [\gcd(i, j) = d] \frac{i \cdot j}{d ^ 2} \\ & = \sum_{d = 1} ^ n \sum_{i = 1} ^ {\left\lfloor\frac{n}{d}\right\rfloor} \sum_{j = 1} ^ {\left\lfloor\frac{n}{d}\right\rfloor} [\gcd(i, j) = 1] i \cdot j \\ & = \sum_{d = 1} ^ n \sum_{i = 1} ^ {\left\lfloor\frac{n}{d}\right\rfloor} \sum_{j = 1} ^ {\left\lfloor\frac{n}{d}\right\rfloor} i \cdot j \sum_{p \mid i, p \mid j} \mu(p) \\ & = \sum_{p = 1} ^ n \mu(p) \sum_{d = 1} ^ n \sum_{i = 1} ^ {\left\lfloor\frac{n}{d}\right\rfloor} \sum_{j = 1} ^ {\left\lfloor\frac{n}{d}\right\rfloor} [p \mid i] \cdot [p \mid j] \cdot i \cdot j \\ & = \sum_{p = 1} ^ n \mu(p) \cdot p ^ 2 \sum_{d = 1} ^ {\left\lfloor\frac{n}{p}\right\rfloor} S\left(\left\lfloor\frac{n}{dp}\right\rfloor\right) ^ 2 \Longrightarrow S(n) = \sum_{i = 1} ^ n i \\ & = \sum_{i = 1} ^ n S\left(\left\lfloor\frac{n}{i}\right\rfloor\right) ^ 2 \sum_{d \mid i} \mu(d)\cdot d ^ 2 \\ \end{align*} $$

上述部分是简单的莫比乌斯反演,具体过程不再赘述。

目前的式子已经可以 $\mathcal O(n \log n)$ 求解了。接下来的优化思路肯定是对 $S$ 数论分块,但是瓶颈为 $\sum_{d \mid i} \mu(d) \cdot d ^ 2$ 的前缀和。很自然地,我们会想到杜教筛

看到 $\mu(d) \cdot d ^ 2$ 这个东西,肯定会想到卷上一个 $g(x) = x ^ 2$ 函数。但是又发现 $f$ 本身也是一个卷积形式,设:

$$ t(x) = \mu(x) \cdot x ^ 2 $$

可以发现 $f$ 函数为:

$$ f = t \ast 1 $$

构造 $h$ 为 $f$ 和 $g$ 的卷积:

$$ \begin{align*} h & = f \ast g \\ & = (t \ast 1) \ast g \\ & = t \ast g \ast 1 \\ & = e \ast 1 \\ & = 1 \end{align*} $$

很惊喜的发现 $h$ 是一个常函数!因为 $t$ 是积性函数,那么 $f = t \ast 1$ 也是积性函数,可以直接线性筛。

直接按照套路上杜教筛就行啦。

时间复杂度:$\mathcal O(n ^ {\frac{2}{3}})$。


Code

#include <cstdio>
#include <unordered_map>

const int N = 2e6 + 5, LIM = 2e6;
const int P = 1e9 + 7, inv2 = 500000004, inv6 = 166666668;

int n, tot, p[N], d[N], f[N], s[N];
bool flg[N];
std::unordered_map<int, int> S;

void add(int &x, int y) {
    (x += y) >= P && (x -= P);
}
void sub(int &x, int y) {
    (x -= y) < 0 && (x += P);
}
void sieve(int n) {
    std::fill(flg + 1, flg + n + 1, true);
    f[1] = 1;
    for (int i = 2; i <= n; i++) {
        if (flg[i]) {
            p[++tot] = i, f[i] = 1 - 1LL * i * i % P;
        }
        for (int j = 1; j <= tot && i * p[j] <= n; j++) {
            flg[i * p[j]] = false;
            if (i % p[j] == 0) {
                f[i * p[j]] = f[i];
                break;
            } else {
                f[i * p[j]] = 1LL * f[i] * f[p[j]] % P;
            }
        }
    }
    for (int i = 1; i <= n; i++) {
        if (f[i] < 0) f[i] += P;
        add(s[i] = f[i], s[i - 1]);
    }
}
int F(int n) {
    if (n <= LIM) return s[n];
    if (S.find(n) != S.end()) return S[n];
    int ans = n;
    auto G = [](int n) {
        return 1LL * n * (n + 1) % P * (n + n + 1) % P * inv6 % P;
    };
    for (int l = 2, r; l <= n; l = r + 1) {
        r = n / (n / l);
        sub(ans, 1LL * (G(r) - G(l - 1) + P) * F(n / l) % P);
    }
    return S[n] = ans;
}
int main() {
    sieve(LIM);
    scanf("%d", &n);
    int ans = 0;
    auto S = [](int n) {
        return 1LL * n * (n + 1) % P * inv2 % P;
    };
    for (int l = 1, r; l <= n; l = r + 1) {
        r = n / (n / l);
        int t = S(n / l);
        add(ans, 1LL * t * t % P * (F(r) - F(l - 1) + P) % P);
    }
    printf("%d\n", 1LL * (ans + n) * inv2 % P);
    return 0;
}

发表评论