1. 程式人生 > 其它 >線性篩求積性函式

線性篩求積性函式

線性篩

線性篩可以在嚴格 \(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\) 的指數。

篩素數:

  1. 判斷 \(n\) 是否標記
  2. 無標記,\(n\) 則為素數,並將 \([n,n^2]\) 打標記
  3. 否則為偶數,直接跳過

莫比烏斯函式:

根據定義:

\[\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]];
        }
    }
}