淺談算法——杜教篩


首先我們給道題目:求\(\sum\limits_{i=1}^n\mu(i)\)

\(n\leqslant 10^5\),我會\(O(n\sqrt{n})\)

\(n\leqslant 10^7\),我會\(O(n)\)線篩!

\(n\leqslant 10^9\),我……

於是杜教篩就此被發明,它可以在\(O(n^{\frac{2}{3}})\)的時間內求出一些積性函數函數的前綴和,如何做?

假定我們現在要求\(S(n)=\sum\limits_{i=1}^nf(i)\),於是我們找來一個積性函數\(g(i)\)(不知道是什么東西),和\(f(i)\)卷積一下,有

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

然后我們求一下卷積形式的前綴和

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

然后我們調整一下枚舉順序,得到

\[\sum\limits_{d=1}^ng(d)\sum\limits_{d|i}f(\dfrac{i}{d}) \]

\[\sum\limits_{d=1}^ng(d)\sum\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}f(i) \]

\[\sum\limits_{d=1}^ng(d)S(\lfloor\dfrac{n}{d}\rfloor) \]

好,我們現在得到\(\sum\limits_{i=1}^n(f*g)(i)=\sum\limits_{d=1}^ng(d)S(\lfloor\dfrac{n}{d}\rfloor)\),然后我們把這個先放着,稍微思考一下,顯然有如下式子

\[g(1)S(n)=\sum\limits_{i=1}^ng(i)S(\lfloor\dfrac{n}{i}\rfloor)-\sum\limits_{i=2}^ng(i)S(\lfloor\dfrac{n}{i}\rfloor) \]

然后你發現中間一個和我們之前推出來的形式是一樣的,於是有

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

如果中間卷積部分的前綴和非常好算,那么我們就可以對后面那部分進行數論分塊,然后遞歸處理,記得記憶化!

我們回到栗子,題目要求的\(f\)就是\(\mu\)對吧,代回去有

\[g(1)S(n)=\sum\limits_{i=1}^n(\mu*g)(i)-\sum\limits_{i=2}^ng(i)S(\lfloor\dfrac{n}{i}\rfloor) \]

現在我們需要找一個優秀的\(g\),使得他們狄利克雷卷積的前綴和非常好算

我們知道

\[\sum\limits_{d|n}\mu(d)=[n=1]=e \]

所以\((1*\mu)=e\),你說\(e\)的前綴和是啥,當然是1啦,於是我們取\(g(x)=1\),有

\[S(n)=1-\sum\limits_{i=2}^nS(\lfloor\dfrac{n}{i}\rfloor) \]

然后我們線篩出一部分\(\mu\)的前綴和,再來一波記憶化搜索,做完了


再舉個栗子,把\(\mu\)換成\(\varphi\)該怎么做?

因為有

\[\sum\limits_{d|n}\varphi(d)=i=id(i) \]

所以還是取\(g(x)=1\),那么得到

\(S(n)=\sum\limits_{i=1}^ni-\sum\limits_{i=2}^nS(\lfloor\dfrac{n}{i}\rfloor)\)

等差數列前綴和\(O(1)\)求出就好了

對於不同的\(f\),只要找到合適的\(g\),就就可以讓你的程序變得非常好寫

不會找沒關系,打個表解決一切

不過記得不要有事沒事想着杜教篩,線篩啥的,埃氏篩法也是很有用的,\(O(n\ln n)\)枚舉倍數也挺好的

騙分過樣例,暴力出奇跡


扯了這么多,講下時間復雜度證明吧

其實可以發現除了\(S(n)=\sum\limits_{i=2}^nS(\lfloor\dfrac{n}{i}\rfloor)\)貢獻了復雜度外,其他基本上可以\(O(1)\)算出答案(惡心的\(g(n)\)不考慮)

那么我們只要算出\(\sqrt n\)\(S(\lfloor\dfrac{n}{i}\rfloor)\)的值即可算出\(S(n)\),於是我們設\(T(n)\)為計算出\(S(n)\)的復雜度,那么有

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

其中\(O(\sqrt n)\)是累加合並的時間

然后我們展開一層,因為更深層的復雜度是高階小量,所以有

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

但由於\(S(n)\)本身是可以通過線篩求出一部分的,假定我們預處理了前\(k\)個的值,且\(k\geqslant \sqrt n\),則復雜度變為

\[T(n)=\sum\limits_{i=1}^{\frac{n}{k}}\sqrt{\dfrac{n}{i}}=O(\dfrac{n}{\sqrt k}) \]

則當\(k\)\(n^{\frac{2}{3}}\)時可以取到較好的復雜度\(T(n)=O(n^{\frac{2}{3}})\)


免責聲明!

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



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