1. 程式人生 > 其它 >淺淡杜教篩

淺淡杜教篩

淺淡杜教篩

狄利克雷卷積

本文的 * 均表示狄利克雷卷積

定義函式 \(f\)\(g\) 狄利克雷卷積後函式的第 \(n\) 項為 \((f*g)(n) = \sum_{d|n}f(d)g(\frac nd)\)

積性函式

如果對於互質的兩數 \(p, q\) 有:\(f(p)f(q) = f(pq)\) ,則認為函式 \(f\)積性函式

特別地,如果對於任意兩數 \(p,q\) 有:\(f(p)f(q) = f(pq)\) ,則認為函式 \(f\)完全積性函式

常見的積性函式有:

  • 元函式 \(\epsilon\)\(\epsilon (n) = [n = 1]\) (完全積性)
  • 恆等函式 \(I\)\(I(n) = 1\) (完全積性)
  • 單位函式 \(id\)\(id(n) = n\) (完全積性)
  • 莫比烏斯函式 \(\mu\)
  • 尤拉函式 \(\varphi\)\(\varphi(n) = \sum_{i = 1} ^ n[\gcd(i, n) = 1]\)
  • 約數個數 \(d\)\(d(n) = \sum_{i = 1} ^ n[i\ |\ n]\)
  • 約數和 \(\sigma\)\(\sigma(n) = \sum_{i = 1}^ni\cdot[i\ |\ n]\)

常見性質有:

  • 對於任意函式 \(f\)\(f * \epsilon = f\)
  • \(\mu * I = \epsilon\)
  • \(\varphi * I = id\)
  • \(\begin{align*} \frac {\varphi(n)}n = \sum_{d|n} \frac{\mu(d)}d \end{align*}\)
杜教篩

已知 \(f\) 為積性函式,求 \(S(n) = \sum_{i = 1} ^ n f(i)\)\(n \le 10^{10}\)

顯然直接做是肯定不行的,考慮構造一個函式 \(g\) ,使得 \(g\) 以及 \(f * g\) 的字首和很好求(可以 \(O(1)\) 求)

找到這樣的函式 \(g\) (一般是上面常見的幾個積性函式或它們的次方),就可以開始推式子了qwq

\[\begin{align*} \sum_{i = 1} ^ n (f * g)(i) = \sum_{i = 1} ^ n \sum_{d | i} f(d) g(\frac id) \end{align*} \]

顯然 \(\frac id\) 的範圍為 \([1,n]\) 所以可以列舉 \(\frac id\) ,用 \(i\) 代替 \(\frac id\) ,可得

\[\begin{align*} \sum_{i = 1} ^ n (f * g)(i) &= \sum_{i = 1} ^ n \sum_{d | i} f(d) g(\frac id) \\ &= \sum_{i = 1} ^ ng(i)\sum_{j = 1} ^ {\lfloor \frac ni\rfloor} f(j) \\ &= \sum_{i = 1} ^ ng(i)S(\lfloor \frac ni\rfloor) \end{align*} \]

由於我們要求 \(S(n)\) ,那麼顯然在上面的式子中當 \(i = 1\) 時有一個 \(S(n)\) ,把它分離出來

\[\begin{align*} \sum_{i = 1} ^ n (f * g)(i) &= \sum_{i = 1} ^ ng(i)S(\lfloor \frac ni\rfloor) \\ &= g(1) S(n) + \sum_{i = 2} ^ n g(i)S(\lfloor \frac ni\rfloor) \end{align*} \]

於是得出關鍵式子:

\[g(1)S(n) = \sum_{i = 1} ^ n (f * g)(i) - \sum_{i = 2} ^ n g(i)S(\lfloor \frac ni\rfloor) \]

可以預處理出 \(M\) 以內的 \(g ,f, f * g\) 的字首和(\(M\) 為線性可接受的值,一般為 \(\sqrt n\)),遞迴求解 \(S(n)\) ,時間複雜度 \(O(n^{\frac 23})\) ,證明不會

大致程式碼結構

inline int sum_g(const int &x) // 求 g(1) + ... + g(x)
{
    ...
}
inline int sum_fg(const int &x) // 求 (f * g)(1) + .. + (f * g)(x)
{
    ...
}
inline int sum_f(const int &x) // 求 S(x)
{
    if (x <= M) return S[x]; // 預處理的值
    if (mp[x]) return mp[x]; // map 記憶化
    int ans = sum_fg(x); // 求 f * g 的字首和
    for (int l = 2, r; l <= x; l = r + 1) // 注意不要從 1 開始
    {
        r = x / (x / l); // 整出分塊
        int res = sum_g(r) - sum_g(l - 1); // g(l) + .. + g(r)
        ans = (ans - res * sum_f(x / l));
    }
    return mp[x] = ans;
}

當然杜教篩難就難在求合適的 \(g\) ,對於 \(g\) 難求的問題,可以用 min_25 篩或 powerful number 篩,但是我完全不會 /kk ,這個只能請教數論代師HS了。

例題

莫比烏斯函式之和

題意:

已知 \(a, b\) ,求:

\[\sum_{i = a} ^ b \mu(i) \]

先求字首和再差分,再根據常用性質\(\mu * I = \epsilon\) ,所以 \(g = I, f * g = \epsilon\) ,直接做

最小公倍數之和 V3

題意:

已知 \(n\) ,求:

\[\sum_{i = 1} ^n \sum_{j = 1} ^n \text{lcm}(i, j) \]

推式子:

\[\begin{align*} &\sum_{i = 1} ^n \sum_{j = 1} ^n \text{lcm}(i, j) \\ =& \sum_{i = 1} ^n \sum_{j = 1} ^n \frac{ij}{\gcd(i, j)} \\ =&\sum_{d = 1} ^ n \sum_{i = 1} ^ {\lfloor \frac n d \rfloor} \sum_{j = 1} ^ {\lfloor \frac n d \rfloor} \frac{id \cdot jd} d [\gcd(i, j) = 1] \\ =&\sum_{d = 1} ^ n d \sum_{i = 1} ^ {\lfloor \frac n d \rfloor} \sum_{j = 1} ^ {\lfloor \frac n d \rfloor} ij [\gcd(i, j) = 1] \\ &\because\sum_{i = 1} ^ n i [\gcd(i, n) = 1] = \frac{\varphi(n)} 2 n \\ \therefore=&\sum_{d = 1} ^ nd\sum_{i= 1} ^ {\lfloor \frac n d \rfloor} i^2\varphi(i) \end{align*} \]

考慮快速求 \(S(\lfloor \frac n d \rfloor) = \begin{align*} \sum_{i= 1} ^ {\lfloor \frac n d \rfloor} i^2\varphi(i) \end{align*}\)

顯然,如果 \(f(i) = i^2\varphi(i)\) ,則 \(g = id^2, f * g = \varphi(i)\) 是個不錯的選擇

兩個整除分塊就行了

約數之和

題意:

已知 \(n\) ,求:

\[\sum_{i = 1} ^ n \sum_{j = 1}^n \sigma(i \cdot j) \]

巨難推,我不會,HS切,Orz