线性筛素数计数
线性筛素数计数
http://ybt.ssoier.cn:8088/problem_show.php?pid=1151
编程求2∼n中有多少个素数。
用线性筛
💡 思路(线性筛 / 欧拉筛)
线性筛的核心思想:
- 每个合数都只会被它的 最小质因子(LPF)筛掉一次;
- 保证全部筛操作的次数为 \(O(n)\) 。
关键规则:
- 枚举
i从 2 到 n - 若
i未被标记,是素数 → 加入素数表 - 对所有已有素数
p:- 标记
i * p为合数 - 如果
p是i的最小质因子(i % p == 0),就停止继续乘更大的质数(确保线性复杂度)
- 标记
✅ C++ 代码(OI 风格 + 详细注释)
#include <bits/stdc++.h>
using namespace std;
// 线性筛模板:统计 2~n 的素数个数
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<int> primes; // 存放所有素数
vector<bool> is_composite(n+1, false); // 标记合数
// 欧拉线性筛
for (int i = 2; i <= n; i++) {
// 若 i 未被标记,则为素数
if (!is_composite[i]) {
primes.push_back(i);
}
// 用当前所有已知素数筛掉 i*p
for (int p : primes) {
// 超界则退出
if (1LL * i * p > n) break;
is_composite[i * p] = true; // 标记为合数
// 若 p 是 i 的最小质因子,则停止继续
// 保证每个合数只被处理一次(线性复杂度核心)
if (i % p == 0) break;
}
}
// 输出素数个数
cout << primes.size() << "\n";
return 0;
}
📌 复杂度分析
- 时间复杂度: \(O(n)\)
每个整数只会被筛一次。 - 空间复杂度: \(O(n)\)
如果你需要版本:输出所有素数 / 多次询问 / 给出素数表上界优化,也可以继续问我!
Prompt:
下面这个代码错在哪里?
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+100;
bool visit[N];
int prime[N],n,x,cnt1;
void init(int n){
int cnt=0;
for(int i=2;i<=n;i++){
if(!visit[i]) prime[cnt++]=i;
for(int j=0;j<cnt;j++){
if(i*prime[j]>n) break;
visit[i*prime[j]]=1;
cnt1++;
if(i%prime[j]==0) break;
}
}
}
int main(){
cin>>n;
init(n);
cout<<cnt1-1;
return 0;
}
Response:
下面指出你代码中的 核心错误与原因,并给出 正确写法。
❌ 你的代码中存在的主要问题
① 你输出的是 cnt1-1,但 cnt1 根本不是素数个数
你把 cnt1 写成:
cnt1++;
它是在 每次筛掉一个数时 +1
也就是「筛选操作次数」,不是「素数数量」!
所以:
cnt1≠ 素数数量cnt1 - 1更是完全没有意义
因此逻辑错误,输出错。
② prime[] 里记录素数数量的变量 cnt 是局部变量
你写了:
int prime[N], n, x, cnt1;
void init(int n){
int cnt = 0; // <-- 局部变量!
问题:
cnt在init()内是局部的main()里无法获取到素数数量prime[]也没有全局保存素数个数信息
所以程序最终无法得知 prime 里存了多少素数。
③ 输出素数个数应该输出的是 cnt
而不是筛掉多少个数。
✅ 正确写法(修正版)
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 100;
bool vis[N];
int prime[N], n, cnt = 0; // cnt 全局,表示素数个数
void init(int n) {
for (int i = 2; i <= n; i++) {
if (!vis[i]) prime[cnt++] = i; // 记录素数
for (int j = 0; j < cnt; j++) {
if (1LL * i * prime[j] > n) break;
vis[i * prime[j]] = true;
if (i % prime[j] == 0) break; // 关键剪枝
}
}
}
int main() {
cin >> n;
init(n);
cout << cnt << "\n"; // 输出素数个数
return 0;
}
📌 总结
| 错误 | 原因 |
|---|---|
cnt1 统计的是筛掉次数 |
不是素数个数,输出错 |
素数计数 cnt 写在函数内部 |
main 拿不到,逻辑错误 |
最终输出 cnt1-1 |
完全不正确 |
正确做法:用全局 cnt 来统计素数数量并输出。

浙公网安备 33010602011771号