AcWing 868. 筛质数
原题链接
简单
作者:
BanLi
,
2021-03-02 19:01:58
,
所有人可见
,
阅读 146
#include<iostream>
using namespace std;
const int N = 1000010;
int primes[N], cnt, n;
bool st[N];
void get_primes(){
for(int i = 2; i <= n; i++){ //遍历需要处理的数
if(!st[i]) //如果遍历到i时还未被筛掉,则i是素数
primes[cnt++] = i;
//从小遍历已经统计的素数
//退出条件理解:变形为pj * i <= n,表示只需要处理到小于等于n的数即可,大于n时就不用处理了
//另外此处不加j<cnt条件见下面的例子总结
for(int j = 0; primes[j] <= n / i; j++){
//此处的想法是利用pj删掉(以pj为最小质因子)的合数,妙处在于借助了i来完成,此处应注意pj * i这个数,勿只单独关注pj,i
//关键点:1. i % pj == 0 pj一定是i的最小质因子,也一定是pj*i的最小质因子
// 2. i % pj != 0 pj一定小于i的所有质因子,pj也一定是pj*i的最小质因子
// 总之,pj总是pj*i的最小质因子,那么pj*i这个数也总是被pj这个最小质因子筛掉
st[primes[j] * i] = true;
//此处需要退出的理解:如果不退出,那么循环继续,后续的数pj*i被pj筛掉时,pj不一定是pj*i最小值因子
//例子:i = 4时,primes中有2,3,利用2筛掉8后此处条件满足应该退出,如果不退出,继续循环就会利用3(pj)筛掉12,
//但是12这个数理应被2筛掉(后续循环到6时会利用2筛掉12)
if(i % primes[j] == 0)
break;
//问:是不是每个合数都会被筛掉?
//答:对于每个合数n,假设pj是n的最小质因子,当i枚举到n/pj(此处一定可以除尽)时,n就会被筛掉,而i是从2开始从小到大枚举的,
// 所以每个合数都会被筛掉。
}
}
}
int main(){
cin >> n;
get_primes();
cout << cnt << endl;
return 0;
}
//一个例子
/* j每次从0开始
i=2, 2未被筛掉,加入primes,primes={2}, 筛掉i*pj(2*2)= 4, i%pj(2%2)== 0,break;
i=3, 3未被筛掉,加入primes,primes={2,3}, 筛掉i*pj(3*2)= 6, i%pj(3%2)!= 0,继续循环,j++;
筛掉i*pj(3*3)= 9, i%pj(3%3)== 0,break;
i=4, 4已被筛掉, primes不变,primes={2,3}, 筛掉i*pj(4*2)= 8, i%pj(4%2)== 0,break;
i=5, 5未被筛掉,加入primes,primes={2,3,5}, 筛掉i*pj(5*2)= 10,i%pj(5%2)!= 0,继续循环,j++;
筛掉i*pj(5*3)= 15,i%pj(5%3)!= 0,继续循环,j++;
筛掉i*pj(5*5)= 25,i%pj(5%5)== 0,break;
......
......
从上面可以看出,对于当前i,如果是合数,那么一定会遇到它的最小质因子,遇到之后就会break;
如果是质数,那么i会被加到primes,且i是primes的最后一个元素,j循环到最后时有i=pj,此时也会满足break条件
总的来说,此循环始终会退出,不需要补充j小于cnt的条件
*/