本文写于2017-10-30,其中的一些思路可能存在问题。本文移至此处时暂未重新学习。
颓了两天,不过luogu橙名了,虽然并没有A题,luogu的名字颜色评测方案实在是成迷。
//线性筛素数#include<iostream>#define PMAXN 10000000using namespace std;bool prime[PMAXN+5]; //prime:0 otherwise:1int primes[PMAXN+5],prime_num=0;void make_prime() //将合数标记为1,其他没有标记的默认为素数{prime[0]=prime[1]=1; //标记0、1为合数for(int i=2;i<=PMAXN;i++) //从2到PMAXN枚举{if(!prime[i]) primes[prime_num++]=i;//若i至此未被标记,说明i是素数,将其放入素数表for(int j=0;j<prime_num;j++)//将i与素数表中的所有元素相乘,结果必为合数{if(i*primes[j]>PMAXN) break;//若当前乘得结果已经超过最大讨论值,直接结束,以防多余运算以及数组越界prime[i*primes[j]]=1;if(i%primes[j]==0) break;//若当前i可以被素数表中的当前元素整除,直接枚举下一个i}}}/*示例:PMAXN=10i prime 0 1 2 3 4 5 6 7 8 9 X prime_num primes- 1 1 0 -2 1 1 0 1 1 23 1 1 0 0 1 1 1 2 2 34 1 1 0 0 1 1 1 1 2 2 35 1 1 0 0 1 0 1 1 1 1 3 2 3 5......*/int main(){make_prime();int n,m;cin>>n>>m;for(int k=1;k<=m;k++){int a;cin>>a;if(prime[a]) cout<<"No"<<endl;else cout<<"Yes"<<endl;}return 0;}
上面的代码对应题目Luogu P3383 【模板】线性筛素数
~~
这里最关键的问题大概就是线性,即保证范围内每一个数(质数和合数)被访问且仅被访问一次。
所以这里有两项操作值得理解:
- prime[i*primes[j]]=1;
- if(i%primes[j]==0) break;
先是第一个,这也是素数筛优化为线性的重要操作之一,也就是筛掉合数时只与当前素数表中的数相乘。如果与一个合数相乘,设这个合数x=pa,其中p是x的质因子,那么ix=ipa必然会在之后i=ia时被筛掉。第二个,如果i%primes[j]==0 则我们可以记i=primes[j]a,如果此时我们继续筛下去,那么下一个筛掉的primes[j]primes[j+1]a会在i=primes[j]*a时被筛掉。
进一步可以发现,我们可以直接不管全部偶数,当判断到偶数的时候直接判断为合数,在预处理的时候能快一些。也可以在生成素数表的时候传入数据范围,也可以省一些时间。下面就是加一些乱七八糟的优化,比如关闭同步啊(别问我读入优化,我不会写),cout改成puts啊什么的,效果还不错。最后的代码:
//线性筛素数#include<iostream>#include<cstdio>using namespace std;bool prime[10000000+5]; //prime:0 otherwise:1int primes[10000000+5],prime_num=0;inline void make_prime(int PMAXN) //将合数标记为1,其他没有标记的默认为素数{prime[0]=prime[1]=1;//标记0、1为合数prime[2]=0;primes[prime_num++]=2;for(register int i=3;i<=PMAXN;i+=2)//从3到PMAXN枚举,显然4及以上的偶数都为合数,所以我们直接不考虑它们{if(!prime[i]) primes[prime_num++]=i;//若i至此未被标记,说明i是素数,将其放入素数表for(register int j=1;j<prime_num;j++)//将i与素数表中的所有元素(除了2)相乘,结果必为合数{if(i*primes[j]>PMAXN) break;//若当前乘得结果已经超过最大讨论值,直接结束,以防多余运算以及数组越界prime[i*primes[j]]=1;if(i%primes[j]==0) break;//若当前i可以被素数表中的当前元素整除,直接枚举下一个i}}}int main(){ios::sync_with_stdio(false);int n,m;cin>>n>>m;make_prime(n+5);for(register int k=1;k<=m;k++){int a;cin>>a;if(prime[a]||(a>2&&a%2==0)) puts("No");//如果我们已经标记过a或者a是大于2的偶数,a则为合数。else puts("Yes");}return 0;}
还是那道题,10个点,第一个代码1716ms,第二个424ms,感觉自己十分的菜。
参考了http://blog.csdn.net/nk_test/article/details/46242401,以及luoguP3383的一些题解。
