莫比乌斯反演笔记

· · 算法·理论

1. 莫比乌斯函数

设正整数 n 的标准分解为 \sum \limits_{i = 1}^{k} p_i^{c_i}

莫比乌斯函数定义为 \mu(n) = \left\{\begin{matrix} 0 \qquad \exists i \in [1, k], c_i \gt 0 \\ (-1) ^ k \qquad \text{otherwise} \end{matrix}\right.

重要结论 1: \sum \limits_{d | n}^{} \mu(d) = [n = 1] 。该结论可以用莫比乌斯函数性质证明。

重要结论 2: 莫比乌斯函数是积性函数,即对于 p \perp q,有 \mu (pq) = \mu(p) \mu(q)

2. 狄利克雷卷积

1. 一些常见的数论积性函数:

其中后三个函数为完全积性函数。

2. 狄利克雷卷积

定义两个数论函数的狄利克雷卷积为 (f * g)(n) = \sum \limits_{d | n} f(d) g(\dfrac{n}{d}),读作 fg

其中后面括号里的 n 代表卷积范围。

卷积有下面这些良好的性质:

上面的性质可以根据卷积定义证明。这里略过。

3. 常见数论函数的狄利克雷卷积

证明:1 * 1 = \sum \limits_{d | n}^{} 1 \times 1 = d(n)

上文提到过,\sum \limits_{d | n} \mu = [n = 1] = \epsilon(n)

证明:\sigma(n) = \sum \limits_{d | n} d = \sum \limits_{d | n} \text{id}(d) \times I(\dfrac{n}{d}) = \text{id} * 1(n)

3. 莫比乌斯反演

莫比乌斯反演的形式如下:

如果存在 F(n) = \sum \limits_{d | n} f(d),那么有 f(n) = \sum \limits_{d | n} F(d) \mu(\dfrac{n}{d})

该结论可以利用狄利克雷卷积来证明。

证明:

### 某些例题: 下面求和的上界省略了下取整符号。 [$\text{P3455 [POI2007]ZAP-Queries}$](https://www.luogu.com.cn/problem/P3455) 求 $\sum \limits_{i = 1}^{n} \sum \limits_{j = 1}^{m} [(i, j) = k]$。 大力莫反。 $$\begin{array}{c} \sum \limits_{i = 1}^{n} \sum \limits_{j = 1}^{m} [(i, j) = k] \\\\ = \sum \limits_{i = 1}^{\frac{n}{k}}\sum \limits_{j = 1}^{\frac{m}{k}} [(i, j) = 1] \\\\ = \sum \limits_{i = 1}^{\frac{n}{k}}\sum \limits_{j = 1}^{\frac{m}{k}} \mu((i, j)) * 1 \\\\ = \sum \limits_{i = 1}^{\frac{n}{k}}\sum \limits_{j = 1}^{\frac{m}{k}} \sum \limits_{d | (i, j)} \mu(d) \\\\ = \sum \limits_{d = 1}^{n} \mu(d) \sum \limits_{i = 1}^{\frac{n}{kd}} \sum \limits_{j = 1}^{\frac{m}{kd}} 1 \\\\ = \sum \limits_{d = 1}^{n} \mu(d) \left \lfloor \dfrac{n}{kd} \right \rfloor \left \lfloor \dfrac{m}{kd} \right \rfloor \end{array}$$ 前面的 $\mu$ 可以线筛,后面的可以整除分块套上 $\mu$ 前缀和。 [$\text{P2522 [HAOI2011]Problem b}$](https://www.luogu.com.cn/problem/P2522) 求 $\sum \limits_{i = a}^{b} \sum \limits_{j = c}^{d} [(i, j) = k]$。 上一道题的强化版。将上一道题写成函数 $\operatorname{calc(n, m, k)}$,表示求 求 $\sum \limits_{i = 1}^{n} \sum \limits_{j = 1}^{m} [(i, j) = k]$ 的值。然后利用类似二维前缀和的方式,计算出答案即为 $\operatorname{calc(b, d, k)} - \operatorname{calc(a - 1, d, k)} - \operatorname{calc(c - 1, b, k)} + \operatorname{calc(a - 1, c - 1, k)}

\text{P1390 公约数的和}

\sum_{i = 1}^n \sum_{j = i + 1}^n (i, j)

首先考虑计算 \sum \limits_{i = 1}^n \sum \limits_{j = 1}^n (i, j)

首先枚举 (i, j),得到 \sum \limits_{d = 1}^{n} d \sum \limits_{i = 1}^{n} \sum \limits_{j = 1}^{n} [(i, j) = d]

继续化简:

\begin{array}{c} \sum \limits_{d = 1}^{n} d \sum \limits_{i = 1}^{n} \sum \limits_{j = 1}^{n} [(i, j) = d] \\\\ = \sum \limits_{d = 1}^{n} d \sum \limits_{i = 1}^{\frac{n}{d}} \sum \limits_{j = 1}^{\frac{n}{d}} [(i, j) = 1] \\\\ = \sum \limits_{d = 1}^{n} d \sum \limits_{i = 1}^{\frac{n}{d}} \sum \limits_{j = 1}^{\frac{n}{d}} \mu * 1(\gcd(i, j)) \\\\ = \sum \limits_{d = 1}^{n} d \sum \limits_{i = 1}^{\frac{n}{d}} \sum \limits_{j = 1}^{\frac{n}{d}} \sum \limits_{p | (i, j)} \mu(p) \\\\ = \sum \limits_{d = 1}^{n} d \sum \limits_{p = 1}^{n} \mu(p) \sum \limits_{i = 1}^{\frac{n}{pd}} \sum \limits_{j = 1}^{\frac{n}{pd}} 1\\\\ = \sum \limits_{d = 1}^{n} d \sum \limits_{p = 1}^{\frac{n}{d}} \mu(p) \left \lfloor \dfrac{n}{pd} \right \rfloor \left \lfloor \dfrac{n}{pd} \right \rfloor \\\\ \text{令 k = pd,} \sum \limits_{k = 1}^{n} \left \lfloor \dfrac{n}{k} \right \rfloor \left \lfloor \dfrac{n}{k} \right \rfloor \sum \limits_{d | k} d \times \mu(\dfrac{k}{d}) \\\\ \sum \limits_{k = 1}^{n} \left \lfloor \dfrac{n}{k} \right \rfloor \left \lfloor \dfrac{n}{k} \right \rfloor \text{id} * \mu(k) \\\\ \sum \limits_{k = 1}^{n} \left \lfloor \dfrac{n}{k} \right \rfloor \left \lfloor \dfrac{n}{k} \right \rfloor \varphi(k) \end{array} 原题要求的就是刚才求的减去 $\sum i$ 之后在除以 $2$ 。这里根据最大公约数的交换律或者对称性易得。 另外,这个题也可以不必卷到 $\varphi$。只要卷到 $= \sum \limits_{d = 1}^{n} d \sum \limits_{p = 1}^{\frac{n}{d}} \mu(p) \left \lfloor \dfrac{n}{pd} \right \rfloor \left \lfloor \dfrac{n}{pd} \right \rfloor$ 就可以套两层整除分块了。这样的复杂度是 $O(n + n) = O(n)$,也很优秀。 一天后返回来在看一眼原来推得式子,会发现确实推复杂了。正难则反,直接用 $\varphi * 1 = id$ 来推。 $$ \begin{array}{c} \sum \limits_{i = 1}^{n} \sum \limits_{j = 1}^{n} (i, j) \\\\ = \sum \limits_{i = 1}^{n} \sum \limits_{j = 1}^{n} \varphi * 1((i,j)) \\\\ = \sum \limits_{i = 1}^{n} \sum \limits_{j = 1}^{n} \sum \limits_{d | (i, j)} \varphi(d) \\\\ = \sum \limits_{d = 1}^{n} \varphi(d) \sum \limits_{i = 1}^{\frac{n}{d}} \sum \limits_{j = 1}^{\frac{n}{d}} 1 \\\\ \sum \limits_{d = 1}^{n} \varphi(d) \left \lfloor \dfrac{n}{d} \right \rfloor \left \lfloor \dfrac{n}{d} \right \rfloor \end{array} $$ 可以发现结果是一样的。 [$\text{P2257 YY的GCD}$](https://www.luogu.com.cn/problem/P2257) 设质数集为 $\mathbb{P}$,求: $$\sum \limits_{i = 1}^{n} \sum \limits_{j = 1}^{m} [(i, j) \in \mathbb{P}]$$ 又到了愉快的推式子时间:这里设 $n \le m$, $$ \begin{array}{c} \sum \limits_{i = 1}^{n} \sum \limits_{j = 1}^{m} [(i, j) \in \mathbb{P}] \\\\ = \sum \limits_{d \in \mathbb{P}}^{} \sum \limits_{i = 1}^{n} \sum \limits_{j = 1}^{m} [(i, j) = d] \\\\ = \sum \limits_{d \in \mathbb{P}}^{} \sum \limits_{i = 1}^{\frac{n}{d}} \sum \limits_{j = 1}^{\frac{m}{d}} [(i, j) = 1] \\\\ = \sum \limits_{d \in \mathbb{P}}^{} \sum \limits_{i = 1}^{\frac{n}{d}} \sum \limits_{j = 1}^{\frac{m}{d}} \mu * 1(\gcd(i, j)) \\\\ = \sum \limits_{d \in \mathbb{P}}^{} \sum \limits_{i = 1}^{\frac{n}{d}} \sum \limits_{j = 1}^{\frac{m}{d}} \sum \limits_{p | (i, j)} \mu(p) \\\\ = \sum \limits_{d \in \mathbb{P}}^{} \sum \limits_{p = 1}^{\frac{n}{d}} \mu(p) \sum \limits_{i = 1}^{\frac{n}{pd}} \sum \limits_{j = 1}^{\frac{m}{pd}} 1\\\\ = \sum \limits_{d \in \mathbb{P}}^{} \sum \limits_{p = 1}^{\frac{n}{d}} \mu(p) \left \lfloor \dfrac{n}{pd} \right \rfloor \left \lfloor \dfrac{m}{pd} \right \rfloor \\\\ \text{令 k = pd,} \sum \limits_{k = 1}^{n} \left \lfloor \dfrac{n}{k} \right \rfloor \left \lfloor \dfrac{m}{k} \right \rfloor \sum \limits_{d | k, d \in \mathbb{P}} \mu(\dfrac{k}{d}) \\\\ \end{array} $$ 然后发现两个下取整可以直接整除分块,瓶颈就在计算 $\sum \limits_{d | k, d \in \mathbb{P}} \mu(\dfrac{k}{d})$ 上。这个玩意可以筛出质数后预处理。 预处理复杂度 $O(n + n \log \log n)$,计算复杂度 $O(T \sqrt n)$。总复杂度 $O(n + n \log \log n + T \sqrt n)$。 看了一下题解,预处理也可以线性筛。不过不想看了,反正能过就行。 ```cpp void init() { mu[1] = 1; for (int i = 2; i <= N - 5; i ++ ) { if (!is_prime[i]) p[ ++ cnt] = i, mu[i] = -1; for (int j = 1; j <= cnt and i * p[j] <= N - 5; j ++ ) { is_prime[i * p[j]] = true; if (i % p[j] == 0) break; mu[i * p[j]] = - mu[i]; } } for (int i = 1; i <= cnt; i ++ ) for (int j = 1; p[i] * j <= N - 5; j ++ ) f[p[i] * j] += mu[j]; for (int i = 1; i <= N - 5; i ++ ) s[i] = s[i - 1] + f[i]; } void substack() { scanf("%lld%lld", &n, &m); if (n > m) swap(n, m); int ans = 0; for (int l = 1, r; l <= n; l = r + 1) { r = min(n / (n / l), m / (m / l)); ans += (n / l) * (m / l) * (s[r] - s[l - 1]); } printf("%lld\n", ans); } ``` [$\text{P3327 [SDOI2015]约数个数和}$](https://www.luogu.com.cn/problem/P3327) 设 $d(x)$ 为 $x$ 的约数个数,给定 $n,m$,求 $$\sum_{i=1}^n\sum_{j=1}^md(ij)$$ 有结论:$d(ij) = \sum \limits_{x | i}^{} \sum \limits_{y | j}^{} [(x, y) = 1]

于是可以愉快地推柿子了。

\begin{array}{c} \sum \limits_{i = 1}^{n} \sum \limits_{j = 1}^{m} \sum \limits_{x | i}^{} \sum \limits_{y | j}^{} [(x, y) = 1] \\\\ = \sum \limits_{x = 1}^{n} \sum \limits_{y = 1}^{m} [(x, y) = 1] \left \lfloor \dfrac{n}{x} \right \rfloor \left \lfloor \dfrac{m}{y} \right \rfloor \\\\ = \sum \limits_{d = 1}^{\min(n, m)} \mu(d) \sum \limits_{x = 1}^{\frac{n}{d}} \sum \limits_{j = 1}^{\frac{m}{d}} \left \lfloor \dfrac{n}{xd} \right \rfloor \left \lfloor \dfrac{m}{yd} \right \rfloor \end{array}

不妨设 f(n) = \sum \limits_{i = 1}^{n} \left \lfloor \dfrac{n}{i} \right \rfloor,可以发现这个玩意就等于 \sum \limits_{i = 1}^{n}d(i)。而这个东西可以筛 \mu 的时候预处理出来。

于是柿子变成 \sum \limits_{d = 1}^{\min(n, m)} \mu(d) f(\dfrac{n}{d}) f(\dfrac{m}{d})。整出分块即可。总复杂度 O(T \sqrt n)。预处理可以用线性筛,但是懒得写了,直接莽了个 O(n \log n) 暴力上去。

void init(int n) {
    mu[1] = 1;
    for (int i = 2; i <= n; i ++ ) {
        if (!is_prime[i]) p[ ++ cnt] = i, mu[i] = -1;
        for (int j = 1; j <= cnt and i * p[j] <= n; j ++ ) {
            is_prime[i * p[j]] = true;
            if (i % p[j] == 0) break;
            mu[i * p[j]] = -mu[i];
        }
    }
    for (int i = 1; i <= n; i ++ )
        mu_s[i] = mu_s[i - 1] + mu[i];
    for (int i = 1; i <= n; i ++ )
        for (int j = i; j <= n; j += i)
            s[j] ++ ;
    for (int i = 1; i <= n; i ++ )
        s[i] += s[i - 1];
}
void substack() {
    scanf("%lld%lld", &n, &m);
    int ans = 0;
    for (int l = 1, r; l <= min(n, m); l = r + 1) {
        r = min(n / (n / l), m / (m / l));
        ans += s[n / l] * s[m / l] * (mu_s[r] - mu_s[l - 1]);
    }
    printf("%lld\n", ans);
}

两年半两天半,终于把莫反学完了。再次小记,以免自己以后再忘掉。