淺談杜教篩


杜教篩模板

杜教篩是用來干蛤的呢?

它可以在非線性時間內求積性函數前綴和。

前置知識

積性函數

積性函數:對於任意互質的整數 \(a,b\)\(f(ab)=f(a)f(b)\) 則稱 \(f(x)\) 的數論函數。

完全積性函數:對於任意整數 \(a,b\)\(f(ab)=f(a)f(b)\) 的數論函數。

  • 常見的積性函數:\(\varphi,\mu,\sigma,d\)
  • 常見的完全積性函數:\(\epsilon,I,id\)

這里特殊解釋一下 \(\epsilon,I,id\) 分別是什么意思:
\(\epsilon(n) = [n=1], I(n) = 1, id(n) = n\)

狄利克雷卷積

\(f, g\) 是兩個數論函數,它們的狄利克雷卷積卷積是:\((f*g)(n) = \sum \limits _{d | n} f(d) g(\frac{n}{d})\)

性質:滿足交換律,結合律

單位元:\(\epsilon\) (即 \(f*\epsilon=f\)

結合狄利克雷卷積得到的幾個性質:

  1. \(\mu * I = \epsilon\)
  2. \(\varphi * I = id\)
  3. \(\mu * id = \varphi\)

莫比烏斯反演

\[g(n) = \sum\limits_{d|n}f(d) \]

\[f(n)=\sum\limits_{d|n}\mu(d)g(\frac{n}{d}) \]

證明:這里需要用到前面提到的性質:\(\mu * I = \epsilon\)

給出的條件等價於 \(g=f * I\)

所以 \(g*\mu=f*I*\mu=f*\epsilon=f\)\(g * \mu = f\) 即 結論。


杜教篩

設現在要求積性函數 \(f\) 的前綴和, 設 \(\sum \limits_{i=1}^{n} f(i) = S(n)\)

再找一個積性函數 \(g\) ,則考慮它們的狄利克雷卷積的前綴和

\[\sum\limits_{i=1}^{n}(f*g)(i) \]

\[\begin{aligned} &= \sum\limits_{i=1}^{n} \sum \limits _{d|i} f(d)g(\frac{i}{d}) \\ &= \sum \limits _{d=1}^{n} g(d)\sum\limits _{i=1}^{\lfloor \frac{n}{d}\rfloor } f(i) \\ &= \sum \limits _{d=1}^{n} g(d) S(\lfloor \frac{n}{d} \rfloor) \end{aligned} \]

其中得到第一行是根據狄利克雷卷積的定義。

得到第二行則是先枚舉 \(d\) 提出 \(g\)

得到第三行則是把 $\sum\limits _{i=1}^{\lfloor \frac{n}{d}\rfloor } f(i) $ 替換為 $S(\lfloor \frac{n}{d} \rfloor) $

接着考慮 \(g(1)S(n)\) 等於什么。

可以發現,他就等於

\[\sum \limits _{i=1}^{n} g(i) S(\lfloor \frac{n}{i} \rfloor) - \sum \limits _{i=2}^{n} g(i) S(\lfloor \frac{n}{i} \rfloor) \]

(可以理解成從1開始的前綴和減去從2開始的前綴和就是第一項)

前面這個式子\(\sum \limits _{i=1}^{n} g(i) S(\lfloor \frac{n}{i} \rfloor)\) ,根據剛才的推導,他就等於 \(\sum\limits_{i=1}^{n}(f*g)(i)\)

所以得到杜教篩的核心式子:

\[g(1)S(n)=\sum\limits_{i=1}^{n}(f*g)(i) - \sum \limits _{i=2}^{n} g(i) S(\lfloor \frac{n}{i} \rfloor) \]

得到這個式子之后有什么用呢?

現在如果可以找到一個合適的積性函數 \(g\) ,使得可以快速算出 \(\sum\limits_{i=1}^{n}(f*g)(i)\)\(g\) 的前綴和,便可以用數論分塊遞歸地求解。

代碼按照理解大概可以寫成這樣(默認 lllong long
(可以理解成一個偽代碼。。就是一個思路的框架)

ll GetSum(int n) { // 算 f 前綴和的函數
  ll ans = f_g_sum(n); // 算 f * g 的前綴和
  // 以下這個 for 循環是數論分塊
  for(ll l = 2, r; l <= n; l = r + 1) { // 注意從 2 開始
    r = (n / (n / l)); 
    ans -= (g_sum(r) - g_sum(l - 1)) * GetSum(n / l);
    // g_sum 是 g 的前綴和
    // 遞歸 GetSum 求解
  } return ans; 
}

這個代碼的復雜度是 \(O(n^{\frac{3}{4}})\),證明如下:

設求出 \(S(n)\) 的復雜度是 \(T(n)\) ,要求出 \(S(n)\) 需要求出 \(\sqrt n\)\(S (\lfloor \frac{n}{i} \rfloor)\) 的值,結合數論分塊的復雜度 \(O(\sqrt n)\) 可得:

\[T(n) = \sum\limits_{i=1}^{\sqrt n} O(\sqrt i) + O(\sqrt {\frac{n}{i}})=O(n^{\frac{3}{4}}) \]

還可以進一步優化杜教篩,即先線性篩出前 \(m\) 個答案,之后再用杜教篩。這個優化之后的復雜度是:

\[T(n) = \sum\limits_{i=1}^{\lfloor \frac{n}{m} \rfloor} \sqrt \frac{n}{i} = O({\frac{n}{\sqrt m}}) \]

\(m = n ^ {\frac{2}{3}}\) 時,\(T(n) = O(n^{\frac{2}{3}})\)

可以使用哈希表來存下已經求過的答案,也可以不用。

考慮到上面的求和過程中出現的都是 $\lfloor \frac{n}{i} \rfloor $ 。開一個大小為兩倍 \(\sqrt n\) 的數組 \(dp\) 記錄答案。如果現在需要求出 GetSum(x) ,若 \(x \leq \sqrt n\) ,返回 dp[x] ,否則返回 dp[sqrt n + n / i] 即可。這樣可以省去哈希表的復雜度。



實戰演練

再掛一次核(tao)心(lu)式,全都要靠它:

\[g(1)S(n)=\sum\limits_{i=1}^{n}(f*g)(i) - \sum \limits _{i=2}^{n} g(i) S(\lfloor \frac{n}{i} \rfloor) \]

它的關鍵就是要找到合適的 \(g\) 使得這個東西可以快速地算。

理論知識大概就這么多,接下來看幾個例子:

(1) \(\mu\) 的前綴和

考慮到莫比烏斯函數的性質 \(\mu * I = \epsilon\) ,自然想到取 \(f=\mu,g=I,f*g=\epsilon\)

其中 \(I\) 的前綴和和 \(\epsilon\) 的前綴和都弱到爆了。。

所以就輕松的解決了。

杜教篩代碼:

inline ll GetSumu(int n) {
  if(n <= N) return sumu[n]; // sumu是提前篩好的前綴和
  if(Smu[n]) return Smu[n]; // 記憶化
  ll ret = 1ll; // 單位元的前綴和就是 1
  for(int l = 2, r; l <= n; l = r + 1) {
    r = n / (n / l); ret -= (r - l + 1) * GetSumu(n / l);
    // (r - l + 1) 就是 I 在 [l, r] 的和
  } return Smu[n] = ret; // 記憶化
}

(2) \(\varphi\) 的前綴和

考慮到 \(\varphi\) 的性質 \(\varphi * I = id\),取 \(f = \varphi, g = I, f * g = id\)

\(f * g\)\(id\) 的前綴和為 \(\frac{n * (n+1)}{2}\)

杜教篩代碼:

inline ll GetSphi(int n) {
  if(n <= N) return sump[n]; // 提前篩好的
  if(Sphi[n]) return Sphi[n]; // 記憶化
  ll ret = 1ll * n * (n + 1) / 2; // f * g = id 的前綴和
  for(int l = 2, r; l <= n; l = r + 1) {
    r = n / (n / l); ret -= (r - l + 1) * GetSphi(n / l);
    // 同上,因為兩個的 g 都是 I 
  } return Sphi[n] = ret; // 記憶化
}

(1) & (2) 就是杜教篩模板 luogu p4213

(3) (綜合)\(\sum\limits_{i=1}^{n}\varphi(i) \cdot i\)

\(f = \varphi \cdot id, g = id\), 考慮迪利克雷卷積的形式得到 \((f*g)(n)=\sum \limits _{d|n} (\varphi(d) \cdot d) \cdot (\frac{n}{d}) = n \sum\limits_{d|n}\varphi(d)=n^2\)

\((f * g)(i) = i^2\)

這樣就可以快速求得 \((f*g)(i)\) 的前綴和 \(\frac{n(n+1)(2n+1)}{6}\)

就可以了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM