線性篩求積性函式
阿新 • • 發佈:2021-08-23
線性篩
線性篩可以在嚴格 \(O(n)\) 的時間內篩出積性函式的值
擁有常見的套路。
假設 \(n = p_1^{a_1} p_2^{a_2} \dots p_k^{a_k}\)
如果我們能快速得到 \(f(p_i),f(p_i^{k+1})\) 的取值,那麼直接套板子即可。
定義:
\(p_i\) 表示 \(n\) 分解質因數後第 \(i\) 個質數,\(a_i\) 表示 \(p_i\) 的指數。
篩素數:
- 判斷 \(n\) 是否標記
- 無標記,\(n\) 則為素數,並將 \([n,n^2]\) 打標記
- 否則為偶數,直接跳過
莫比烏斯函式:
根據定義:
\[\mu =\begin{cases}\left( -1\right) ^{k}\left( n=p_{1}p_{2}\ldots p_{k}\right) \\ 0\left( \exists P^{2}|n\right) \\ 1\left( n=1\right) \end{cases} \]const int MAXN = 1e4 + 10; int N, prime[MAXN], vis[MAXN], mu[MAXN], tot; void GetMu(int N) { vis[1] = mu[1] = 1; for(int i = 2; i <= N; i++) { if(!vis[i]) prime[++tot] = i, mu[i] = -1; for(int j = 1; j <= tot && i * prime[j] <= N; j++) { vis[i * prime[j]] = 1; if(!(i % prime[j])) { mu[i * prime[j]] = 0; break; } mu[i * prime[j]] = mu[i] * mu[prime[j]]; //根據莫比烏斯函式的定義,這裡也可以寫為 //mu[i * prime[j]] = -mu[i]; } } }
尤拉函式:
int N, prime[MAXN], vis[MAXN], phi[MAXN], tot; void GetPhi(int N) { vis[1] = phi[1] = 1; for(int i = 2; i <= N; i++) { if(!vis[i]) prime[++tot] = i, phi[i] = i - 1; for(int j = 1; j <= tot && i * prime[j] <= N; j++) { vis[i * prime[j]] = 1; if(!(i % prime[j])) { phi[i * prime[j]] = phi[i] * prime[j]; break; } phi[i * prime[j]] = phi[i] * phi[prime[j]]; } } }
約數個數:
我們需要考慮最小的質因子對每個數的貢獻。
\[n = p_1^{a_1} p_2^{a_2} \dots p_k^{a_k} \]設 \(d(i)\) 表示 \(i\) 的約數個數,我們根據約數定理:
\[d(i) = \prod_{i = 1}^k (a_i + 1) \]記 \(a(i)\)表示 \(n\) 的最小的質因子 \((a_1)\) 的指數, \(d(pi)=2\)
當 \(i\) 時,考慮 \(i∗p_j\) ,實際上也就是 \(a_i\) 的指數多了1
我們先除去原來的,再加上新的就彳亍了
int N, prime[MAXN], vis[MAXN], D[MAXN], a[MAXN], tot; void GetD(int N) { vis[1] = D[1] = a[1] = 1; for(int i = 2; i <= N; i++) { if(!vis[i]) prime[++tot] = i, D[i] = 2, a[i] = 1; for(int j = 1; j <= tot && i * prime[j] <= N; j++) { vis[i * prime[j]] = 1; if(!(i % prime[j])) { D[i * prime[j]] = D[i] / (a[i] + 1) * (a[i] + 2); a[i * prime[j]] = a[i] + 1; break; } D[i * prime[j]] = D[i] * D[prime[j]]; a[i * prime[j]] = 1; } } }
約數和:
我們設 \(SD(i)\) 表示 \(i\) 的約數和,有式子:
\[SD(n) = \prod_{i = 1}^k (\sum_{j = 1}^{a_i} p_i^j) \]\(sum(i)\) 表示 \(i\) 最小的質因子的貢獻,即 \(sum(i) = \sum_{i = 1}^{a_1}p_1^j\)
\(low(i)\) 表示 \(i\) 最小質因子的指數,\(low(i)=a_1\)
有了這三個我們就可以轉移了
同樣是考慮 \(i\) 的最小的因子對答案的貢獻
int N, prime[MAXN], vis[MAXN], SD[MAXN], sum[MAXN], low[MAXN], tot;
void GetSumD(int N) {
vis[1] = SD[1] = low[1] = sum[1] = 1;
for(int i = 2; i <= N; i++) {
if(!vis[i]) prime[++tot] = i, sum[i] = SD[i] = i + 1, low[i] = i;
for(int j = 1; j <= tot && i * prime[j] <= N; j++) {
vis[i * prime[j]] = 1;
if(!(i % prime[j])) {
low[i * prime[j]] = low[i] * prime[j];
sum[i * prime[j]] = sum[i] + low[i * prime[j]];
SD[i * prime[j]] = SD[i] / sum[i] * sum[i * prime[j]];
break;
}
low[i * prime[j]] = prime[j];
sum[i * prime[j]] = prime[j] + 1;
//這裡low和sum不是積性函式
SD[i * prime[j]] = SD[i] * SD[prime[j]];
}
}
}