题目描述
本题已更新,从判断素数改为了查询第k小的素数
提示:如果你使用 cin 来读入,建议使用 std::ios::sync_with_stdio(0) 来加速。
如题,给定一个范围n,有 q个询问,每次输出第k小的素数。
输入格式
第一行包含两个正整数 n q,分别表示查询的范围和查询的个数。
接下来 q 行每行一个正整数k ,表示查询第 k 小的素数。
输出格式
输出 q 行,每行一个正整数表示答案。
样例
【样例输入】
100 5
1
2
3
4
5
【样例输出】
2
3
5
7
11
一些想法
主函数部分:优化输入输出。输入两个数,调用函数找从 2~n 中的质数。输出要求大小的质数。(记得换行)
自定义函数部分:(设定全局变量数组,bool 标记是不是质数,int 储存质数,还定义一个数计数有几个质数)初始化标记数组全部是 true(假装一开始全是质数),特殊标记 1 不是质数。
循环找质数,如果标记这个数是质数,先将计数器加一,然后将这个数放进数组。
然后循环,根据线性筛法将质数×当前数(质数或合数)标记为合数(非质数)。因为每一个合数都可以分解为:合数×质数或质数×质数。
优化部分:当前数能够整除一个质数,说明这个质数是当前数的最小质因子(第一个能整除的),所以后面的合数应该由更小的数标记,要退出(避免重复)。以次来达到让每个合数恰好被其最小质因子标记一次的目的。
AC代码
cpp
#include<bits/stdc++.h>
using namespace std;
bool b[100000010];
int cnt=0;
int a[6000010];
void zs(int n){
memset(b,true,sizeof(b));
b[1]=false;
for(int i=2;i<=n;i++){
if(b[i]==true){
a[++cnt]=i;
}
for(int j=1;j<=cnt&&(long long)i*a[j]<=n;j++){
b[a[j]*i]=false;
if(i%a[j]==0) break;
}
}
}
int main(){
std::ios::sync_with_stdio(0);
cin.tie(0);//优化处理
int n,q;
cin>>n>>q;
zs(n);
while(q--){
int k;
cin>>k;
cout<<a[k]<<"\n";
}
return 0;
}
易错点
1.在核心循环时, 条件 i*a[j]<=n 没有强制转换类型,导致测试点过大爆掉。
因为,i*a[j]<=n 有可能会超出 int 范围(很大),所以要以防万一,强制转换为 long long 类型,这样就不会爆了。
2.没有写自定义函数,而是直接在主函数里写核心过程。只要测试点大一点,就会超时(TLE)。
因为:函数封装使代码结构更清晰,便于编译器优化(如内联、缓存利用),减少冗余计算;而无函数时代码逻辑混乱,易重复执行无效操作,且难以利用编译器优化,导致运行效率下降。
3.将 bool b[100000010] ;和 int a[6000010]; 设为局部变量,导致爆栈,但是开小一点又过不了大的测试点。
所以,要设为全局变量才行,因为全局变量在程序开始时从全局存储区分配静态内存空间,只要系统可用内存足够,一般能正常分配;而若作为局部变量在主函数等函数内部定义,其从栈中分配内存,栈空间有限,大数组容易导致栈溢出从而报错,所以在主函数等函数内部定义会爆,定义为全局变量在程序开始时一般不会爆。
4.用 vector 会超时。
这里用一维数组就不会超时。因为 vector 访问速度慢,而全局数组在程序启动时一次性分配,无运行时开销,而且 vector 在函数内定义大vector,频繁的内存分配/释放造成开销。vector的动态扩容机制(倍增策略)也会导致临时内存需求翻倍。但是一维数组连续内存布局,完美适配CPU缓存预取机制,还可以用 memset 快速初始化(比vector构造函数快得多)。
所以,对于固定规模的大规模数据处理,优先使用全局数组而非vector。