线性筛其实NOIp2017之前打过线性筛板子,不过那时候就没理解,临阵磨枪顾不了那么多。
学习数论的时候用到了,又看了一遍。作为一个性子比较急的人找了好几个博客都看不下去,因此花了很长时间才搞懂。
O(sqrt(n))判断n是不是质数就不多赘述了。素数筛之所以叫“筛”是因为它的基本思想是对于1-n批量判素数,由当前已知素数筛去后面的是该素数倍数的合数,以此剩下质数。因此,根据这一基本思想,我们可以初步写出一种粗劣的素数筛算法:
int n; const int sz=1e6+5; bool h[sz]={0};//h[i]=0表示i是质数,反之不是 int z[sz],len;//z用于储存已知素数,len表示已知素数的个数 h[1]=1; for(int i=2;i<=n;i++){ if(h[i]==0)z[++len]=i; for(int j=1;j<=len&&z[j]*i<=n;j++)h[z[j]*i]=1; }
然后大家会发现,n一大这个算法的效率就非常呵呵了……
尝试优化这个算法。
分析可以发现,这个算法的效率之所以会低,是因为同一个数有很多质因子,因此会被筛很多遍。如果我们可以让一个数只被筛一次,就可以保证效率优化到O(n)。(这不是废话吗摔)
那么,如何让一个数只被筛一次呢?可以考虑只用这个数最小的质数或者最大的质数去筛这个数。我们的原算法是,对于素数p,晒去p*x(x是大于1的正整数),若p是(p*x)的最小质因数,只要使得p不大于x中的所有质因数即可。原算法可以看作是枚举x,对于每个x从小到大枚举p,我们只要在p增大到恰为x的最小质因数时去枚举下一个x即可。
代码如下:
int n; const int sz=1e6+5; bool h[sz]={0};//h[i]=0表示i是质数,反之不是 int z[sz],len;//z用于储存已知素数,len表示已知素数的个数 h[1]=1; for(int i=2;i<=n;i++){ if(h[i]==0)z[++len]=i; for(int j=1;j<=len&&z[j]*i<=n;j++){ h[z[j]*i]=1; if(i%z[j]==0)break; } }
日常写错……欢迎指正
QQ:2960005671
邮箱:bleavescoder@163.com