线性筛求积性函数

线性筛

线性筛可以在严格 \(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. 否则为偶数,直接跳过
    代码就不放了

线性筛

namespace Prime{
    int vis[N],prime[N],cnt;
    void main(){
        for(int i=2;i<=n;i++){
            if(vis[i]==0) prime[++cnt]=i;
            for(int j=1;j<=cnt&&prime[j]*i<=n;j++){
                vis[prime[j]*i]=1; 
                if(i%prime[j]==0) break;
            } 
        }
    }
}

莫比乌斯函数:

根据定义:

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

vis[1]=mu[1]=1;
for(int i=2;i<=n;i++){
    if(!vis[i]) prime[++cnt]=i,mu[i]=-1;//素数,即为1
    for(int j=1;j<=cnt&&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];//有奇数个质因子,
    }
}

欧拉函数:

推导过程:

转化成幂次形式,则有以下推论:

  1. \(i\) 为素数, \(phi[i]=i-1\)

  2. \(i\% prime[j]=0\) , \(phi[i \times prime[j]]=phi[i]*prime[j]\)

  3. \(i\% prime[j]!=0\) ,\(phi[i \times prime[j]]=phi[i]*(prime[j]-1)=phi[i]*phi[prime[j]]\)

    vis[1]=phi[1]=1;
    for(int i=2;i<=n;i++){
        if(!vis[i]) prime[++cnt]=i,phi[i]=i-1;
        for(int j=1;j<=cnt&&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(p_i)=2\)

\(i\) 时,考虑 \(i \times 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]];
        }
    }
}
posted @ 2021-08-23 18:16  Evitagen  阅读(154)  评论(0编辑  收藏  举报