线性筛

线性筛

  线性筛真是一种很神奇的科技.有的函数单个求非常慢,但是因为有一些优秀的性质,所以可以线性筛,从而将均摊复杂度降为$O(1)$.

  积性函数都可以线性筛(据说).要线筛应该满足这样的性质:

  可以快速的求出$f(1),f(p),f(p^k)$.这样一来就可以方便的求出其他所有函数值了.不过实际应用中主要关注的是两个有倍数关系的函数值之间的关系.

  从刚刚的性质可以看出,所有的线性筛都必须以线性筛素数为基础(设想求素数需要$NlogN$,那么后面为了降低复杂度的所有操作就都没用了)

  

  现在从线性筛素数开始学习线性筛.

  首先根据一般性的做法,写出式子:

  $f(n)= \left\{\begin{matrix}0,n=1 \\ 1,n=p \\0,n=p^k \end{matrix}\right.$

  然后就没了啊...

  下面是代码:

  
1 for (R i=2;i<=n;++i)
2 {
3     if(!vis[i]) pri[++h]=i;
4     for (R j=1;j<=h&&i*pri[j]<=n;++j)
5     {
6         vis[ i*pri[j] ]=1;
7         if(i%pri[j]==0) break;
8     }
9 }
prime

 

  这里比较有趣的是第7行,它保证了时间复杂度.如果每个数只被自己最小的那个质因子筛掉就可以满足线性复杂度.对于第7行,如果条件为真,说明$pri(j)$是$i$的最小质因子,就不应该再用$i$去筛其它的数了,因为这些数的最小质因子都应该是$pri(j)$.

  

  莫比乌斯函数:

  $\mu(n)= \left\{\begin{matrix}1,n=1 \\ -1,n=p \\0,n=p^k \end{matrix}\right.$

  
 1 for (R i=2;i<=maxn;++i)
 2     {
 3         if(!vis[i])
 4             pri[++h]=i,mu[i]=-1;
 5         for (R j=1;j<=h&&i*pri[j]<=maxn;++j)
 6         {
 7             vis[ i*pri[j] ]=1;
 8             if(i%pri[j]==0) break;
 9             mu[ i*pri[j] ]=-mu[i];
10         }
11     }
mu

 

  欧拉函数:

  
 1 for(R i=2;i<n;++i)
 2 {
 3     if(f[i])
 4     {
 5         pri[++h]=i;
 6     phi[i]=i-1;
 7     }
 8     for(R j=1;j<=h&&i*pri[j]<n;++j)
 9     {
10         if(i%pri[j]==0)
11         {
12             phi[i*pri[j]]=phi[i]*pri[j];
13             break;
14         }
15         phi[i*pri[j]]=phi[i]*(pri[j]-1);
16     }
17 }
phi

 

  约数个数:

  这里先说一个利用唯一分解定理求单个数字的约数个数的方法.

  $n=\prod p_i^{a_i},d(n)=\prod(1+a_i)$

  其实这里已经用到了积性函数的思想.

  线性筛:$d(n)= \left\{\begin{matrix}1,n=1 \\ 2,n=p \\k+1,n=p^k \end{matrix}\right.$

  实际运用中还要记录每个数最小质因子的出现次数。

  
 1 d[1]=1;
 2 for (R i=2;i<=n;++i)
 3 {
 4     if(!vis[i]) pri[++h]=i,d[i]=2,m[i]=1;
 5     for (R j=1;j<=h&&i*pri[j]<=n;++j)
 6     {
 7         vis[ i*pri[j] ]=1;
 8         if(i%pri[j]==0)
 9     {
10         m[ i*pri[j] ]=m[i]+1;
11             d[ i*pri[j] ]=d[i]/(m[i]+1)*(m[i]+2);
12         break;
13      }
14     d[ i*pri[j] ]=d[i]*2;
15     m[ i*pri[j] ]=1;
16     }
17 }
d

 

  约数和:

  $\sigma(n)= \left\{\begin{matrix}1,n=1 \\ p+1,n=p \\ \sum_{i=0}^k p^i,n=p^k \end{matrix}\right.$

  那么对于每一个数需要保存它的最小质因子的那一项的等比数列,一种比较好想的做法是同时保留$p^k$,转移时把这一项乘上一个$p$再加进等比数列,下面是代码。

  
 1 d[1]=1;
 2     for (R i=2;i<=n;++i)
 3     {
 4         if(!vis[i]) pri[++h]=i,m[i]=i,t[i]=1,d[i]=i+1,p[i]=i,s[i]=i+1;
 5         for (R j=1;j<=h&&i*pri[j]<=n;++j)
 6         {
 7             vis[ i*pri[j] ]=true;
 8             if(i%pri[j]==0)
 9             {
10                 m[ i*pri[j] ]=pri[j];
11                 t[ i*pri[j] ]=t[i]+1;
12                 p[ i*pri[j] ]=p[i]*pri[j];
13                 s[ i*pri[j] ]=s[i]+p[ i*pri[j] ];
14                 d[ i*pri[j] ]=d[i]/s[i]*s[ i*pri[j] ];
15                 break;
16             }
17             d[ i*pri[j] ]=d[i]*(pri[j]+1);
18             t[ i*pri[j] ]=1;
19             s[ i*pri[j] ]=1+pri[j];
20             p[ i*pri[j] ]=pri[j];
21             m[ i*pri[j] ]=pri[j];
22         }
23     }
sigma

  但是这样会使代码变得很长,而且常数比较大,考虑等比数列后再填一项还可以怎么转移,平移一下!$(1+p+p^2+...+p^k) \times p+1=1+p+p^2+...+p^{k+1}$

  
 1 d[1]=1;
 2     for (R i=2;i<=n;++i)
 3     {
 4         if(!vis[i]) pri[++h]=i,d[i]=i+1,s[i]=i+1;
 5         for (R j=1;j<=h&&i*pri[j]<=n;++j)
 6         {
 7             vis[ i*pri[j] ]=true;
 8             if(i%pri[j]==0)
 9             {
10                 s[ i*pri[j] ]=s[i]*pri[j]+1;
11                 d[ i*pri[j] ]=d[i]/s[i]*s[ i*pri[j] ];
12                 break;
13             }
14             d[ i*pri[j] ]=d[i]*(pri[j]+1);
15             s[ i*pri[j] ]=1+pri[j];
16         }
17     }
sigma-2

 

  那么来看一道例题:

  $f(n)=\sum_{d|n}d^k\mu(\frac{n}{d})$

  想一下-------

  $f(n)= \left\{\begin{matrix}1,n=1 \\ p^k-1,n=p \\p^k\times f(p^{k-1}),n=p^k \end{matrix}\right.$

  
 1 for (R i=2;i<=maxn;++i)
 2     {
 3         if(!vis[i]) mu[i]=-1,pri[++h]=i,g[i]=(f[i]-1+mod)%mod,ls[i]=i;
 4         for (R j=1;j<=h&&i*pri[j]<=maxn;++j)
 5         {
 6             vis[ i*pri[j] ]=1;
 7             ls[ i*pri[j] ]=pri[j];
 8             if(i%pri[j]==0)
 9             {
10                 g[ i*pri[j] ]=g[i]*f[ pri[j] ]%mod;
11                 break;
12             }
13             mu[ i*pri[j] ]=-mu[i];
14             g[ i*pri[j] ]=(g[i]*g[ pri[j] ])%mod;
15         }
16     }
bzoj 4407

 

  以后看到什么有趣的函数筛再补充吧.

posted @ 2018-11-29 22:00  shzr  阅读(328)  评论(0编辑  收藏  举报