zoukankan      html  css  js  c++  java
  • 线性筛

    线性筛

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

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

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

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

      

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

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

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

      然后就没了啊...

      下面是代码:

      
    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{egin{matrix}1,n=1 \ -1,n=p \0,n=p^k end{matrix} ight.$

      
     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{egin{matrix}1,n=1 \ 2,n=p \k+1,n=p^k end{matrix} ight.$

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

      
     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{egin{matrix}1,n=1 \ p+1,n=p \ sum_{i=0}^k p^i,n=p^k end{matrix} ight.$

      那么对于每一个数需要保存它的最小质因子的那一项的等比数列,一种比较好想的做法是同时保留$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) imes 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^kmu(frac{n}{d})$

      想一下-------

      $f(n)= left{egin{matrix}1,n=1 \ p^k-1,n=p \p^k imes f(p^{k-1}),n=p^k end{matrix} ight.$

      
     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

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

  • 相关阅读:
    C# 关键字 之 virtual
    Set NOCOUNT 和 SET XACT_ABORT
    radl+ReportViewer (转)
    销售利润分析案例
    实现动态展现报表:2种开发思路
    jdk,tomcat,myecplise程序安装步骤
    BI报表工具选型的整理总结
    MyEclipse 快捷键
    oracle biee
    跨数据库
  • 原文地址:https://www.cnblogs.com/shzr/p/10041221.html
Copyright © 2011-2022 走看看