【代码超详解】洛谷 P3383 【模板】线性筛素数(题目已更新 · 数据已加强)(欧拉筛,总时间:4.42 s)

一、题目描述

在这里插入图片描述

二、算法分析说明与代码编写指导

事实上在此之前我一直使用自己写的埃氏筛法的模板来快速打出前若干个质数:

unsigned prime[6542] = { 2,3 }, _PrimeTy, MaxPrime, * prime_end = prime + sizeof(prime) / sizeof(prime[0]);
inline void gen_prime() {
	decltype(_PrimeTy) a = 4, t; bool flag = true;
	for (auto i = prime + 2; i != prime_end;) {
		t = sqrt(a), flag = true;
		for (auto j = prime; *j <= t; ++j)if (a % *j == 0) { flag = false; break; }
		if (flag) { *i = a, ++i; }
		++a;
	}
	MaxPrime = *(prime_end - 1);
}

以及配套的判断质数的模板:

inline bool isprime(const decltype(_PrimeTy)& x) {
	if (x <= MaxPrime)return binary_search(prime, prime_end, x);
	else {
		static decltype(_PrimeTy) t; t = min((decltype(_PrimeTy))sqrt(x), MaxPrime);
		for (auto j = prime; *j <= t; ++j)if (x % *j == 0)return false;
		return true;
	}
}

在这里插入图片描述在本题的数据更新之前,该方法写的代码(传送门)甚至比一些欧拉筛的代码表现出了更优秀的耗时:
在这里插入图片描述从上图可见,在本题数据和要求更新之前,这种改进实现方法的埃氏筛法的平均总耗时约 200 ms,而当时排在首页的前几个题解的总耗时超过 700 ~ 800 ms 甚至超过了 1 s(当时提交了许多次,而且一些提交的代码略有不同,因为我在做微调以尽可能优化)。
但是今天在做 LightOJ 1259 时,我发现如果直接套这个模板会导致超时。即使在打表后通过 bitset 来 O(1) 判定 1 ~ 10^7 内的任意一个数是否为质数,该题的耗时也超过了 1900 ms,远远高于网上的其它题解。
然后我开始找一些题打算重新对模板进行测试,无意中发现 P3383 的数据和要求都变动了。
本题中 n = 10^8,查表得 10^8 以内的质数共有 5761455 个。如果直接套用上面给的埃氏筛的模板,打 10^8 以内的质数要接近 14 s(release 模式下)。所以,必须换用时间复杂度更低的方法。
在这里插入图片描述
在这里插入图片描述

三、AC 代码(总时间:4.42 s)

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<bitset>
#pragma warning(disable:4996)
using namespace std;
unsigned prime[5761455], _PrimeTy, MaxPrime, * prime_end = prime; bitset<100000001> notprime;
inline void gen_prime() {
	decltype(_PrimeTy) n = notprime.size() - 1, m = n / 2, L; notprime[0] = notprime[1] = 1;
	for (decltype(_PrimeTy) i = 2; i <= m; ++i) {
		if (!notprime[i]) { *prime_end = i, ++prime_end; }
		L = n / i;
		for (auto j = prime; j != prime_end && *j <= L; ++j) {
			notprime[i * *j] = true; if (i % *j == 0)break;
		}
	}
	for (decltype(_PrimeTy) i = m + 1; i <= n; ++i)
		if (!notprime[i]) { *prime_end = i, ++prime_end; }
	MaxPrime = *(prime_end - 1);
}
unsigned* Prime = prime - 1, n, q, k;
int main() {
	gen_prime();
	scanf("%u%u", &n, &q); ++q;
	while (--q) {
		scanf("%u", &k);
		printf("%u\n", Prime[k]);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值