Min_25筛学习笔记
引入問題:求一個積性函數\(f(i)\)的前綴和
\[ \sum_{i=1}^nf(i) \]
其中\(f(i)\)滿足對質數\(p\)有\(f(p)\)是關于\(p\)的低次多項式、\(f(p^c)\)的值可以快速求出。
Min_25篩時間復雜度為\(O(\frac{n^\frac{3}{4}}{\log n})\),空間復雜度為\(O(\sqrt n)\),實現常數較小。
Min_25篩的大體思想是將1~n之間的數分為三部分:質數、合數和(第三部分你猜啊)1
Part1.處理質數
設\(\mathrm{prime}(i)\)表示返回\(i\)是否為質數。
對于每個\(\lfloor\frac n x\rfloor\),我們先預處理出
\[ \sum_{i=2}^{\lfloor\frac n x\rfloor}[\mathrm{prime}(i)]f(i) \]
由于對于質數\(p\),\(f(p)\)可以被表示為若干個\(p^k\)的和,所以我們可以把多項式每一項分別求值再相加,所以我們現在只考慮\(f(p)=p^k\)的情況。注意這里\(f\)是完全積性函數。
設\(\mathrm{minp}(i)\)表示\(i\)質因數分解后最小質因子,\(p_i\)代表從小到大第\(i\)個質數。設
\[ g(a,b)=\sum_{i=2}^a[\mathrm{prime}(i)\or\mathrm{minp}(i)>p_b]i^k \]
直觀地說,\(g(a,b)\)就是做\(b\)輪埃氏篩法后\(2\)~\(a\)中剩下的所有數的\(k\)次冪的和。設\(|P|\)為\(2\)~\(\sqrt n\)內素數集合的大小,則有
\[ \sum_{i=2}^{\lfloor\frac n x\rfloor}[\mathrm{prime}(i)]i^k=g(\lfloor\frac n x\rfloor,|P|) \]
從小到大枚舉質數,模擬埃氏篩法的過程,從\(g(?,b-1)\)推導\(g(?,b)\),不難得到
\[ g(a,b)=\begin{cases}g(a,b-1)&a<p_b^2\\g(a,b-1)-p_b^k\left(g(\lfloor\frac a {p_b}\rfloor,b-1)-g(p_{b-1},b-1)\right)&a\ge p_b^2\end{cases} \]
什么意思呢??
若\(a<p_b^2\),所有\(a\)以內的合數已經被篩掉了,所以不影響,直接轉移即可。
若\(a\ge p_b^2\),我們考慮在第\(b\)輪篩法中篩掉了哪些數。
每一個\(\le\lfloor\frac a{p_b}\rfloor\)的且不含有\(\le p_b\)的質因子的數的\(p_b\)倍都會被篩掉
考慮減去\(g(\lfloor\frac a{p_b}\rfloor,b-1)\),但由于這之中包含了\(<p_b\)質數,我們在加上\(g(p_{b-1},b-1)\)加回來即可。
由于\(p_{b-1}<\sqrt n\),所以合法的\(\lfloor\frac n x\rfloor\)中一定存在\(p_{b-1}\)
由于最后我們只需要所有\(\lfloor\frac n x \rfloor\)的\(g\)值來作為答案,并且在遞推過程中只會用到這些值 所以只需要記錄\(O(\sqrt n)\)個\(\lfloor \frac n x\rfloor\)遞推即可
即對于每個\(\lfloor\frac n x\rfloor\),我們求出了\(\sum_{i=2}^{\lfloor\frac n x\rfloor}[\mathrm{prime}(i)]f(i)=\sum_kg(\lfloor\frac n x \rfloor,|P|)\),\(|P|\)代表\(\sqrt n\)以內的素數集合的大小。
Part2.計算答案
令
\[ S(a,b)=\sum_{i=2}^a[\mathrm{minp}(i)\ge p_b]f(i) \]
我們分為兩部分處理。
計算\(S(a,b)\)中所有質數的貢獻,即\(\sum_{i=2}^n[\mathrm{minp}(i)\ge p_b\and\mathrm{prime}(i)]f(i)\)
這部分可通過處理的\(g\)快速得到:即為\(g(a,|P|)-g(p_{b-1},|P|)\)。
計算\(S(a,b)\)中所有合數的貢獻
我們枚舉最小質因子\(p_i\)和出現次數\(t\)
\[ \sum_{i=b}^{|P|}\sum_{p_i^{t+1}\le a}\left(S(\lfloor\frac a {p_t^i}\rfloor,i+1)*f(p_i^t)+f(p_i^{t+1})\right) \]
注意后面加上的那玩意兒是因為\(S\)中1不被考慮在內。
所以:
\[ S(a,b)=\begin{cases} 0,&a<p_b\\ \displaystyle g(a,|P|)-g(p_{b-1},|P|)+\sum_{i=b}^{|P|}\sum_{p_i^{t+1}\le a}\left(S(\lfloor\frac a {p_t^i}\rfloor,i+1)*f(p_i^t)+f(p_i^{t+1})\right) ,&a>p_b\end{cases} \]
據說這個直接搜就行了,不加記憶化都跑得飛快-_-||
實現
我們可以預處理所有的\(\lfloor \frac n x\rfloor?\)的值,并從小到大記為\(a_1,\dots,a_m?\)
對于\(1\le i\le \sqrt n\),有\(a_i=i\)
對于其它的\(i\)有\(\lfloor\frac n {a_i}\rfloor=m-i+1\)
這樣我們可以實現\(O(1)\)從數字到下標的變換
在處理\(g\)數組我們直接用\(a\)數組中的下標存儲,空間復雜度即\(O(\sqrt n)\)。
轉移\(g\)的時候只需要從大到小枚舉(就像01背包那樣,這樣直接在原數組轉移,滾都不用滾了),對于\(a< p_b^2\)的那部分不用搭理。。。時間復雜度是\(O(\frac{n^{\frac 3 4}}{\log n})\)的,不會證。
轉移\(S\)的時候直接枚舉,遞歸就行(雷哥說是\(O((n^2)^{n^2})\)的復雜度),復雜度也是\(O(\frac{n^{\frac 3 4}}{\log n})\),也不會證。
例子
SPOJ-DIVCNTK
給定\(n,k\),設\(\sigma_0(x)\)表示\(x\)的約數個數,求\(\sum_{i=1}^n\sigma_0(i^k)\),滿足\(n,k\le 10^{10}\),答案ull自然溢出。
//SPJ DIVCNTK #include <cstdio> #include <cmath> using namespace std;long long n, sqt, k; long long a[2000010], g[2000010]; int prime[1000010]; int m, tot;int getid(long long x) { return x <= sqt ? x : m - n / x + 1; }unsigned long long chuans(long long n, int b) {if (n < prime[b]) return 0;unsigned long long ans = (k + 1) * (g[getid(n)] - g[getid(prime[b - 1])]);for (int i = b; i <= tot && prime[i] * (long long)prime[i] <= n; i++)for (long long x = prime[i], f = k + 1; x * prime[i] <= n; x *= prime[i], f += k)ans += (chuans(n / x, i + 1) + 1) * f + k;return ans; }int main() {int t;scanf("%d", &t);while (t --> 0){scanf("%lld%lld", &n, &k);sqt = sqrt(n);tot = m = 0;for (long long i = 1; i <= n; i = a[m] + 1)a[++m] = n / (n / i), g[m] = a[m] - 1;for (int i = 2; i <= sqt; i++)if (g[i] != g[i - 1]){prime[++tot] = i;long long sqr = i * (long long)i;for (int j = m; a[j] >= sqr; j--)g[j] -= g[getid(a[j] / i)] - g[i - 1];}printf("%llu\n", chuans(n, 1) + 1);}return 0; }轉載于:https://www.cnblogs.com/oier/p/10287574.html
總結
以上是生活随笔為你收集整理的Min_25筛学习笔记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [bzoj3676] [APIO2014
- 下一篇: ESP8266 01S WIFI 网络