杜教筛 学习笔记

杜教筛 学习笔记

膜拜 dls orz。

Dirichlet 卷积

Dirichlet 卷积,即狄利克雷卷积,对于两个数论函数 \(f, g\),定义他们的 Dirichlet 卷积 \((f * g)\) 为:

\[(f*g)(n) = \sum_{d | n} f(d)g(\dfrac{n}d) \]

对于两个积性函数,它们的 狄利克雷卷积 也是积性函数。

Dirichlet 卷积符合交换律,结合律,分配律。

杜教筛

用于求一些数论函数 \(f\) 的前缀和:

\[S(n) = \sum_{ i= 1}^n f(i) \]

考虑 \(h = f* g\)\(*\) 表示 Dirichlet 卷积。

则对 \(h\) 求前缀和:

\[\begin{aligned} \sum_{i = 1}^n h(i)&= \sum_{i = 1}^n \sum_{d | i}g(d)f(\dfrac{i}d)\\ \sum_{i = 1}^n h(i)&= \sum_{d = 1}^n \sum_{d | i}^ng(d)f(\dfrac{i}d)\\ \sum_{i = 1}^n h(i)&= \sum_{d = 1}^n g(d)\sum_{d | i}^nf(\dfrac{i}d)\\ \sum_{i = 1}^n h(i)&= \sum_{d = 1}^n g(d)\sum_{i = 1}^{\lfloor\frac{n}d\rfloor}f(i)\\ \sum_{i = 1}^n h(i)&= \sum_{d = 1}^n g(d)S(\lfloor\frac{n}d\rfloor)\\ \sum_{i = 1}^n h(i)&= g(1)S(n) + \sum_{d = 2}^n g(d)S(\lfloor\frac{n}d\rfloor)\\ g(1)S(n)&= \sum_{i = 1}^n h(i) - \sum_{d = 2}^n g(d)S(\lfloor\frac{n}d\rfloor)\\ \end{aligned} \]

可以发现,如果 \(h\) 的前缀和可快速求出,\(g\) 的前缀和可以快速求出,就可以使用数论分块递归地计算 \(S(n)\)

如果预处理前 \(m = n^{\frac23}\)\(g, h\) 的前缀和,时间复杂度可以达到 \(O(n^{\frac23})\),证明困难,略去。

例子

约定 \(\text{id}(n) = n, 1(n) = n, \varepsilon(n) = [n = 1]\)

欧拉函数

\(f = \varphi\) 时,可以构造 \(\text{id}=\varphi*1\),套杜教筛公式可以得到:

\[S_\varphi(n) = \sum_{i = 1}^n 1-\sum_{d = 2}^nS_\varphi(\lfloor\frac{n}d\rfloor)\\ S_\varphi(n) = \dfrac{n(n + 1)}2 -\sum_{d = 2}^nS_\varphi(\lfloor\frac{n}d\rfloor)\\ \]

可以快速计算。

莫比乌斯函数

\(f = \mu\) 时,可以构造 \(\varepsilon=\mu*1\),套杜教筛公式可以得到:

\[S_\varphi(n) = \sum_{i = 1}^n [n = 1]-\sum_{d = 2}^nS_\varphi(\lfloor\frac{n}d\rfloor)\\ S_\mu(n) = 1 -\sum_{d = 2}^nS_\mu(\lfloor\frac{n}d\rfloor)\\ \]

可以快速计算。

C++ 实现

以下是 P4213 【模板】杜教筛 的代码实现。

// Problem: P4213 【模板】杜教筛
// Contest: Luogu
// Author: Moyou
// Copyright (c) 2023 Moyou All rights reserved.
// Date: 2023-11-28 23:23:03

#include <algorithm>
#include <cstring>
#include <iostream>
#include <queue>
#include <unordered_map>
#define int long long
using namespace std;
const int N = 1664520; // n^{2/3}

int primes[N], tot;
int phis[N], mus[N];
unordered_map<int, int> phi, mu;
bool st[N];
void sieve() {
    mus[1] = phis[1] = 1;
    for(int i = 2; i <= N - 5; i ++) {
        if(!st[i]) primes[++ tot] = i, mus[i] = -1, phis[i] = i - 1;
        for(int j = 1; j <= tot && primes[j] * i <= N - 5; j ++) {
            st[i * primes[j]] = 1;
            if(i % primes[j] == 0) {
                phis[i * primes[j]] = phis[i] * primes[j];
                break;
            }
            phis[i * primes[j]] = phis[i] * (primes[j] - 1), mus[i * primes[j]] = -mus[i];
        }
    }
    for(int i = 1; i <= N - 5; i ++) mus[i] += mus[i - 1], phis[i] += phis[i - 1];
}
int Duphi(int n) {
    if(n <= N - 5) return phis[n]; // 不加这个会被卡常 qwq
    if(phi.count(n)) return phi[n];
    int ans = n * (n + 1) / 2;
    for(int l = 2, r; l <= n; l = r + 1) {
        r = n / (n / l);
        ans -= (r - l + 1) * Duphi(n / l);
    }
    return phi[n] = ans;
}
int Dumu(int n) {
    if(n <= N - 5) return mus[n];
    if(mu.count(n)) return mu[n];
    int ans = 1;
    for(int l = 2, r; l <= n; l = r + 1) {
        r = n / (n / l);
        ans -= (r - l + 1) * Dumu(n / l);
    }
    return mu[n] = ans;
}

int n;
void work() {
    cin >> n;
    cout << Duphi(n) << ' ' << Dumu(n) << '\n';
    return ;
}

signed main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    sieve();
    int T = 1; 
    cin >> T;
    while (T--) work();

    return 0;
}

posted @ 2023-11-29 00:17  MoyouSayuki  阅读(17)  评论(0编辑  收藏  举报
:name :name