[復習]莫比烏斯反演,杜教篩,min_25篩


[復習]莫比烏斯反演,杜教篩,min_25篩

莫比烏斯反演

做題的時候的常用形式:

\[\begin{aligned}g(n)&=\sum_{n|d}f(d)\\f(n)&=\sum_{n|d}\mu(\frac{d}{n})g(d)\end{aligned} \]

實際上還有

\[\begin{aligned}g(n)&=\sum_{d|n}f(d)\\f(n)&=\sum_{d|n}\mu(\frac{n}{d})g(d)\end{aligned} \]

證明可以看看這里,只需要把式子帶回去就可以證了。
式子很簡單,做題很有用,技巧很豐富。

主要通過題目來寫這一部分的內容。

  • 【BZOJ1101】ZAP
    題意:求\(\displaystyle \sum_{i=1}^a\sum_{j=1}^b[gcd(i,j)==d]\)
    題解:
    \(\displaystyle f(d)=\sum_{i=1}^a\sum_{j=1}^b[gcd(i,j)==d],g(n)=\sum_{n|d}f(d)\)
    那么通過式子的含義不難知道\(\displaystyle g(d)=\sum_{i=1}^a\sum_{j=1}^b[d|gcd(i,j)]\)
    那么推導就很簡單了

\[\begin{aligned} g(d)&=\sum_{i=1}^a\sum_{j=1}^b[d|gcd(i,j)]\\ &=\sum_{i=1}^{a/d}\sum_{j=1}^{b/d} [1|gcd(i,j)]\\ &= \lceil \frac{a}{d} \rceil* \lceil \frac{b}{d} \rceil\\ f(d)&=\sum_{d|i}\mu(\frac{i}{d})g(i)\\ &=\sum_{d|i}\mu(\frac{i}{d})\lceil \frac{a}{i} \rceil* \lceil \frac{b}{i} \rceil\\ &=\sum_{i=1}^{min(a,b)/d}\mu(i)\lceil \frac{a/d}{i} \rceil* \lceil \frac{b/d}{i} \rceil\\ \end{aligned}\\ \]

考慮最終這個求和式,如果直接單次\(O(min(a,b)/d)\)的計算,那么復雜度是\(O(Tmin(a,b)/d)\)的,是會\(TLE\)的。
實際上,當\(i\)\([1,n]\)時,\(n/i\)向下取整的取值只有大約\(2\sqrt n\)個左右,所以對於相同的值完全可以合並在一起算,所以我們只需要處理出\(\mu\)的前綴和即可直接數論分塊計算,把單次復雜度優化到了\(\sqrt {min(a,b)}\)級別。

\[\begin{aligned} Ans&=\sum_{i=1}^n\sum_{j=1}^m lcm(i,j)\\ &=\sum_{i=1}^n\sum_{j=1}^m \frac{ij}{gcd(i,j)}\\ &=\sum_{d=1}^n\frac{1}{d}\sum_{i=1}^n\sum_{j=1}^m [gcd(i,j)=d]ij\\ &=\sum_{d=1}^nd\sum_{i=1}^{n/d}\sum_{j=1}^{m/d} [gcd(i,j)=1]ij \end{aligned}\]

\(\displaystyle f(d,n,m)=\sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)=d]ij,g(n)=\sum_{n|d}f(d)\)

\[\begin{aligned} g(d)&=\sum_{i=1}^n\sum_{j=1}^m[d|gcd(i,j)]ij\\ &=d^2\sum_{i=1}^{n/d}\sum_{j=1}^{m/d}ij\\ &=d^2 S([\frac{n}{d}])S([\frac{m}{d}]) \end{aligned}\]

其中\(\displaystyle S(n)=\sum_{i=1}^ni=\frac{n(n+1)}{2}\)
那么題目中的式子要求的是\(\displaystyle f(1)=\sum_{i=1}^n\mu(i)g(i)\)
帶回到式子中去,可以得到:

\[\begin{aligned} Ans&=\sum_{d=1}^nd\sum_{i=1}^{n/d}\sum_{j=1}^{m/d} [gcd(i,j)=1]ij\\ &=\sum_{d=1}^nd\sum_{i=1}^{n/d}\mu(i)i^2S([\frac{n}{id}])S([\frac{m}{id}]) \end{aligned}\]

到了這一步,可以做到\(O(n)\)的復雜度了,提前預處理后面整塊的前綴和,然后數論分塊計算答案即可。
但是如果要復雜度更加優秀的話,我們需要繼續推導。

\[\begin{aligned} Ans&=\sum_{d=1}^nd\sum_{i=1}^{n/d}\mu(i)i^2S([\frac{n}{id}])S([\frac{m}{id}])\\ &=\sum_{T=1}^nS([\frac{n}{T}])S([\frac{m}{T}])\sum_{d|T,id=T}d\mu(i)i^2\\ &=\sum_{T=1}^nS([\frac{n}{T}])S([\frac{m}{T}])\sum_{i|T}\frac{T}{i}\mu(i)i^2\\ &=\sum_{T=1}^nS([\frac{n}{T}])S([\frac{m}{T}])T\sum_{i|T}i\mu(i) \end{aligned}\]

不難發現最后那一部分是一個積性函數,那么\(\displaystyle T\sum_{i|T}i\mu(i)\),可以在線性時間里面篩出值並算出前綴和,所以對於前半部分數論分塊,可以做到單次詢問\(O(\sqrt n)\)
考慮這里比前面單詞詢問線性的優化在了哪里,之前那個的確可以做到回答答案是根號級別的,但是因為每次的\(n,m\)的值都不同,導致不得不需要對於每一次不同的詢問做一次前綴和,而后面那個則每次需要預處理的東西都是一樣的,因此只需要全局做一次預處理即可。
想想這個操作的本質,其實就是數論分塊的對象不同而已,因此當有多個和式出現的時候,要考慮清楚到底對於誰進行數論分塊,能夠達到最好的效果。

現在可以來總結一下。
我們仔細想想,這類題目的難點在哪里呢?
首先是推式子,但是這個熟練之后很簡單。注意對於不同的東西數論分塊可以得到不同的復雜度。
其次就是為了數論分塊求函數的前綴和了。其實這個算不上太難,如果數據范圍到了\(1e7\),顯然只能線性篩,那就通過線性篩的本質來考慮,如果當前加入的是一個從未出現過的質因子,那么直接乘積即可,否則的話考慮額外加入一個重復質因子時的貢獻。對於賦初值而言,只需要單獨考慮質數的貢獻就好了。如果數據范圍並沒有到達\(1e7\),如果在\(1e6\)的范圍內,不要一味考慮線性篩,這個復雜度下,調和級數的埃氏篩也是可行的。同時要對於一些積性函數的狄利克雷卷積有所了解,這樣推式子的時候會方便很多。
這里也提供幾個比較常用的式子。
\(1\)表示常數,即\(1(i)=1\)
\(e\)表示單位元,\(e(i)=[i==1]\)
\(id\)表示這個數本身,\(id(i)=i\)
\(\mu,\varphi\)就不說了。

  • \(\displaystyle (1*\mu)(n)=\sum_{d|n}\mu(d)=e\)
    證明並不難,\(\mu\)考慮的只有單個質因子,當某個質因子出現超過了\(1\)\(\mu\)的值就是\(0\)。那么我們假設\(\displaystyle n=\prod_{i=1}^k p_i^{a_i}\)。那么我們真正有用的值只有\(2^k\)個。當\(k>0\)的時候,顯然\(\mu=1\)\(\mu=-1\)是一一對應的,因此和為\(0\),當\(k=0\)的時候,\(n=1\),此時結果為\(1\)。因此這兩個的狄利克雷卷積就是單位元。

  • \(\displaystyle (\mu*id)(n)=\sum_{d|n}d\mu(\frac{n}{d})=\varphi(n)\)
    其實換種寫法就很好證明了,\(\displaystyle \sum_{d|n}\frac{n}{d}\mu(d)\)
    \(\mu\)函數又可以稱為容斥系數,那么這鬼玩意的本質就是\(n\)減去其大於\(1\)的約數的倍數,也就是與\(n\)互質的數的個數,也就是\(\varphi\)

  • \(\displaystyle (1*\varphi)(n)=\sum_{d|n}\varphi(d)=id(n)=id\)
    可以用狄利克雷卷積的方法證明:\(\displaystyle1*\varphi=1*\mu*id=e*id=id\)

  • \(\displaystyle \sum_{i=1}^n [gcd(i,n)=1]i\)
    即小於\(n\)並且與\(n\)互質的數之和。除了莫比烏斯反演可以推之外,還有一個很好的推導方法。假設\(x\)\(n\)互質,那么\(n-x\)\(n\)互質,因此所有與\(n\)互質的數可以兩兩配對,且和為\(n\)。因此上述式子等於\(\displaystyle \frac{n\varphi(n)}{2}\)

杜教篩

求解積性函數前綴和。
假設給定的積性函數為\(f(x)\),設它的前綴和\(\displaystyle S(n)=\sum_{i=1}^n f(i)\)
顯然不能直接求解我們才會單獨把它拿出來考慮。
既然不能直接求解的話,肯定要找一個什么東西來過度,假裝我們找到了一個積性函數\(g(x)\)
把兩者卷積起來:

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

然后考慮求解這個玩意的前綴和:

\[\begin{aligned} \sum_{i=1}^n (g*f)(i)&=\sum_{i=1}^n\sum_{d|i}g(d)f(\frac{i}{d})\\ &=\sum_{d=1}^ng(d)\sum_{j=1}^{n/d}f(i)\\ &=\sum_{d=1}^ng(d)S([\frac{n}{d}]) \end{aligned}\]

然后我們驚奇的發現有這么一個式子:

\[\begin{aligned} g(1)S(n)&=\sum_{i=1}^ng(i)S([\frac{n}{i}])-\sum_{i=2}^ng(i)S([\frac{n}{i}])\\ &=\sum_{i=1}^n (f*g)(i)-\sum_{i=2}^n g(i)S(\frac{n}{i}) \end{aligned}\]

那么假裝我們可以快速的計算\((f*g)\)的前綴和,那么后半部分可以數論分塊+遞歸求解。
這樣一來我們就可以還原出\(S(n)\)了。
所以。。。實際上杜教篩就是一個構造的過程。
那么能夠快速計算前綴和要有多好求呢?比如說我們要求\(\mu\)的前綴和,那么最好可以\(O(1)\)求,而\(\mu*1=e\)\(e\)的前綴和就是\(1\),那就很好辦了。令\(f(x)=\mu(x),g(x)=1\),可以得到

\[\begin{aligned} S(n)&=1-\sum_{i=2}^nS([\frac{n}{i}]) \end{aligned}\]

這個東西?遞歸算算就好了。當然了,別少了記憶化。
推式子熟練了就很好了,而構造\(g\)的時候就記住前面講的幾個常用的狄利克雷卷積的式子,往那個上面靠。這里隨便推一道題目可以看看是怎么構造的。

\[\begin{aligned} Ans&=\sum_{i=1}^n\sum_{j=1}^nijgcd(i,j)\\ &=\sum_{d=1}^n d^3\sum_{i=1}^{n/d}\sum_{j=1}^{n/d}ij[gcd(i,j)=1]\\ &=\sum_{d=1}^n d^3\sum_{i=1}^{n/d}\mu(i)*i^2*S^2([\frac{n}{id}])\\ &=\sum_{T=1}^n S^2([\frac{n}{T}])\sum_{d|T} d^3(\frac{T}{d})^2\mu(\frac{T}{d})\\ &=\sum_{T=1}^n S^2([\frac{n}{T}])(T^2\sum_{d|T}d\mu(\frac{T}{d})\\ &=\sum_{T=1}^n S^2([\frac{n}{T}])(T^2\varphi(T))\\ \end{aligned}\]

考慮怎么求\(f(T)=T^2\varphi(T)\)的前綴和,這個東西顯然是一個積性函數。
發現式子中帶\(\varphi\),所以杜教篩配\(g\)的時候往\(\displaystyle \sum_{d|T}\varphi(T)=T\)上面靠。

\[\begin{aligned} (f*g)(n)&=\sum_{d|n}f(d)g(\frac{n}{d})\\ &=\sum_{d|n}d^2\varphi(d)g(\frac{n}{d})\\ \end{aligned}\]

因為要往只有\(\varphi\)上靠,所以令\(g(x)=x^2\)

\[\begin{aligned} (f*g)(n)&=\sum_{d|n}d^2\varphi(d)g(\frac{n}{d})\\ &=n^2\sum_{d|n}\varphi(d)\\ &=n^3 \end{aligned}\]

那就很好辦了,\(\displaystyle \sum_{i=1}^n i^3=(1+2+...+n)^2\)可以\(O(1)\)計算,那么直接杜教篩就可以快速計算前綴和了。

那么杜教篩就是一個具有很強的構造性的東西,這也從一些方面上使得其並不是那么的實用。

min_25篩

\(min\_25\)篩用於計算積性函數前綴和\(\displaystyle S(n)=\sum_{i=1}^n f(i)\)
相比於杜教篩而言,\(min\_25\)篩的題目更加靈活多變
所求的積性函數要滿足兩個條件

  • \(f(x)\)\(x\)為質數的時候要存在一個多項式的表示方法。
  • \(f(x^c)\)\(x\)為質數的時候能夠快速計算。

至於為什么是這兩個條件,與\(min\_25\)篩的過程之間有着密切的聯系。

既然上述的條件中,我們已經把質數單獨分割出來了,那么我們應該明白,\(min\_25\)篩的過程是一個分解過程,即把所有數分成兩類,質數以及合數,當然還有特殊的\(1\)
這里提前說明幾個東西,底下直接把\(P\)定義為質數集合,\(P_i\)表示的是第\(i\)大的質數。

  • 質數部分
    \(g(n,j)\)表示的是,小於等於\(n\)的所有數\(i\)中,其最小質因子\(p\gt P_j\)或者\(i\)本身就是質數的所有的\(i\)\(f(i)\)之和。
    等等,前面不是說了只有當\(i\)是質數的時候才能方便的計算\(f(i)\)嗎?別急,這里我們假裝所有數都是質數,帶入到是質數的式子中一起計算,換句話說,這里就是一個構造過程,不需要管那么多啦QwQ。或者這樣想,如果我們知道了\(g(n,\infty)\),顯然這個東西就是所有質數的和啊QaQ。接下來往下面看吧。
    考慮一下這個東西怎么樣才能轉移呢,顯然我們的轉移從\(j-1\)轉移到了\(j\),那么就考慮\(g(n,j-1)\)\(g(n,j)\)多算了些什么。
    多算的部分顯然就是那些最小質因子恰好為\(P_{j}\)的數(別忘了上面那個是大於號),那么最小的一個最小質因子為\(P_{j}\)的數是誰呢?\(P_{j}^2\)。如果\(P_j^2\)\(n\)都要大了,那么我們什么也沒有減掉,意味着\(g(n,j)=g(n,j-1)\)
    否則的話,想想我們減掉了什么呢?我們先給所有數除掉一個\(P_j\),那么如果剩下的部分的最小質因子還小於等於\(P_j\)的話那么顯然不合法,也就是\(\displaystyle g([\frac{n}{P_j}],j-1)\),但是這樣子減多了,把小於\(P_j\)的質數的貢獻給減掉了,這些質數與\(P_j\)構成的合數的最小質因子小於\(P_j\),顯然是早就被減掉了。那么我們重新把第\(1\)個到第\(j-1\)個質數的貢獻加回來,也就是\(g(P_j-1,j-1)\)
    然后我們要求的是\(g\)的和,我們減去的是\(P_j\)因子的貢獻,而\(f(x)\)為積性函數,所以轉移可以寫成:

\[g(n,j)=\begin{cases} g(n,j-1)&P_j^2>n\\ g(n,j-1)-f(P_j)(g([\frac{n}{P_j}],j-1)-g(P_j-1,j-1))&P_j^2\leq n \end{cases}\]

前面也說了,\(f(x)\)\(x\)為質數的情況下可以寫成一個多項式的形式,所以其實這個求\(g\)的本質上可以理解為求\(f(x)=x^k\)的一個過程。同時,每次都是從\(j-1\)轉移到\(j\),仔細思考,這個過程很類似於埃氏篩法,每次篩去了一個質數的所有倍數的貢獻。
那么如果最終要計算出所有質數的貢獻,那么顯然就是\(g(n,\infty)\),事實上我們只需要算到\(\sqrt n\) 以內的所有質數就行了。同時,因為上述轉移的過程中是整除,其實在計算過程中的\(n\)的取值也只有\(2\sqrt n\)個左右,也只需要離散后記錄這些位置就行了。
\(g\)的初值也就是\(g(n,0)\)自己想想怎么計算吧QwQ。

  • 合數部分
    知道了質數的貢獻,而目標函數又是積性函數,所以我們只需要用所有的質數拼出所有的合數就可以計算答案了。
    \(S(n,j)\)表示所有最小質因子大於等於\(P_j\)的數\(i\)\(f(i)\)的和,注意這里和上面\(g\)的描述的區別。
    那么計算\(S\)的值的時候顯然是先把質數的貢獻給算上,這一部分的貢獻是\(\displaystyle g(n,|P|)-\sum_{i=1}^{j-1}f(P_j)\),其中\(P\)是小於等於\(\sqrt n\)的質數集合。
    接下來考慮合數的貢獻,顯然每個合數都存在一個最小質因子,那么我們來枚舉這個因子,假設為\(P_k,k\geq j\),枚舉其冪,假設為\(e\),那么考慮的就是所有包含了\(P_k^e\)的合數的貢獻。
    因為\(f\)是積性函數,所以把這里每次就考慮只包含\(P_k^e\)的貢獻,首先是\(\displaystyle S([\frac{n}{P_k^e}],k+1)\),因為這里並沒有包含\(1\)的貢獻,意味着\(P_k^e\)本身的貢獻沒有計算進來,所以要額外加進來。
    所以轉移就可以寫成:

\[S(n,j)=g(n,|P|)-\sum_{i=1}^{j-1}f(P_i)+\sum_{k\geq j}^{|P|}\sum_{e}(f(P_k^e)S([\frac{n}{P_k^e}],k+1)+f(P_{k}^{e+1})) \]

所以底下就來丟幾道題目吧。。。

  • 【LOJ#6053】簡單的函數
    發現這個東西是\(f(p^k)=p\oplus k\),對於質數而言\(f(p)=p-1\),當然\(2\)比較特殊\(f(2)=p+1=3\),所以我們先把它當成\(p-1\)算,最后再加上\(2\)就好了。
    前面也說了,因為在質數上涉及到的是一個多項式計算,所以本質上要求解的就是\(f(p)=p^k\)的形式,那么從這個式子就知道我么要求的是\(k=0\)\(k=1\)的情況。
    \(g(n,j)\)表示\(k=1\)的情況。即\(f(p)=p\),那么顯然有轉移:

\[g(n,j)=\begin{cases} g(n,j-1)&P_j^2>n\\ g(n,j-1)-P_j(g([\frac{n}{P_j}],j-1)-g(P_j-1,j-1))&P_j^2\le n \end{cases}\]

\(h(n,j)\)表示\(k=0\)的情況,即\(f(p)=1\)時的計算,那么有轉移:

\[h(n,j)=\begin{cases} h(n,j-1)&P_j^2>n\\ h(n,j-1)-(h([\frac{n}{P_j}],j-1)-h(P_j-1,j-1))&P_j^2\le n \end{cases}\]

顯然有\(g(n,|p|)-h(n,|p|)\)為所有質數的答案,假設其為\(F(n)\)
那么設\(S(n,j)\)表示\(\displaystyle \sum_{i=2}^n f(i)\),其中\(i\)的最小質因子大於等於\(P_j\)
得到轉移:

\[S(n,j)=F(n)-\sum_{i=1}^{j-1}f(P_j)+\sum_{k\geq j}\sum_e(f(P_k^e)S([\frac{n}{P_k^e}],k+1)+f(P_k^{e+1})) \]

那么簡單的遞歸處理即可。
講講幾個實現的地方,首先要篩的質數只有\(\sqrt n\)以內的,所以完全可以把他們的\(f\)的前綴和給求出來,這也就是\(g\)\(S\)中轉移看起來不好算的兩部分的計算方法。其次,計算\(g(n,j)\)的時候,真正有用的\(n\)只有\(2\sqrt n\)個,因為這個過程你可以看成一個數論分塊的過程,因此提前把\(n\)給數論分塊,得到所有有用的\(n\),這樣子可以節約大量空間。第三,發現\(g\)的轉移的中,之和\(j-1\)相關,意味着在開數組的時候只需要開一維,第二維可以滾掉,並且實際上最終\(g\)要用的也只有\(g(n,|P|)\),所以滾掉就好了。最后一個,計算\(S\)的時候不需要記憶化。
這題代碼我重寫了一遍,直接戳LOJ的提交記錄吧

  • 【UOJ#188】sanrd
    \(\displaystyle \sum_{i=l}^r f(i)\)的值,其中\(f(i)\)表示\(i\)的次大質因子。
    看起來這里的這個函數與質數無關,並且也不是一個積性函數了,那么怎么辦呢?
    首先實際上我們要求的還是這個函數的前綴和,考慮一下\(min\_25\)篩最終求解答案的過程,每次我們枚舉其當前擁有的最小質因子。那么,通過當前計算的\(S(n,j)\)\(j\),我們可以很容易的知道當前數的上一個質因子是\(P_{j-1}\)。那么如果以\(P_{j-1}\)為質因數為貢獻的話,顯然就是當前剩下的數中質數的個數,這個可以提前用\(min\_25\)的前半部分篩出來。否則以更大的質數為貢獻,那么枚舉當前的最小質因子把它除掉接着遞歸處理即可,注意\(P_k^c\)的這個數的次大質因子為\(P_k\),這里的貢獻是額外算的。
    代碼直接戳UOJ記錄


免責聲明!

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



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