【笔记】素数筛

0.前言

写篇博客凑数doge


1.正文

首先是朴素算法,直接爆栗枚举,如果除得尽,就是合数,反之是素数。

一个小小的优化:枚举范围从2(因为所有正整数数都是1的倍数)到 n \sqrt{n} n
即可(因为如果有比 n \sqrt{n} n 更大的因数,那该因数一定能和之前一个比 n \sqrt{n} n
小的n的因数配对)。

int pd(int x){
	for (int i = 2; i <= sqrt(x); i++)
		if (x%i == 0) return 0;
	return 1;
}

上述说法是有缺陷的,因为每个数都是从2枚举到 n \sqrt{n} n ,如果题目数据范围大,很有可能会超时。

所以接下来有一种更快的算法:埃氏筛

对于每一个合数,一定存在一个m|n,其中m是n的最小素因子。利用这一特性,对每一个素数,枚举ta的倍数,将ta们都标记成合数,之后不再理会。

便于理解的资料

void pd(int x, int prime[], int &cnt){
	for (int i = 2; i <= x; i++){
		if (prime[i]) for (int j = i*i; j <= x; j += i) prime[j] = 0;
	}
}

但是埃氏筛依然有缺陷,就是在标记过程中会重复标记,比如,2的倍数中有18,3的倍数中也有18,那么18就会被重复标记,导致效率下降。

那么另一种复杂度更优的做法欧拉筛(又称线性筛)来了。

欧拉筛:只用该合数的最小质数因子筛去这个数,所以一个数最多被筛掉一次,复杂度较前者更优。对于每一个当前数,枚举之前找到的质数,枚举当前数的素数倍,标记为合数。这样保证了每个数只被ta最小的素因子筛一次(因为大的素数会比小的素数慢找到)。欧拉筛只能筛到1e8。

便于理解的资料

const int MAXN = 1e6+7;
bool vis[MAXN];
int cnt, prime[MAXN];

void init(){
	memset(vis, true, sizeof(vis));
	vis[0] = vis[1] = false;
}

void pd(){
	init();
	for (int i = 2; i <= MAXN; i++){
		if(vis[i]) prime[++cnt] = i;
		for (int j = 1; j<=cnt && prime[j]*i<=MAXN; j++){
			vis[prime[j]*i] = false;
			if (i%prime[j] == 0) break;
		}
	}
}

一些模板题

p5723
p3383
p5736

进阶

P1403
P1835
UVA10140


2.结语

慢慢复习,慢慢恢复,慢慢变强。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值