按埃氏筛的实际删数顺序模拟,统计第 k 次真正删除的整数。
OJ: luogu
题目 ID: P6421
难度:普及-
标签:模拟数学
日期: 2026-06-18 20:56
题意
按题目给定的埃拉托色尼筛法删除 2..n 中的数,要求输出第 k 个被删除的整数。
思路
先看一个可以直接验证想法的朴素解:
每一轮先找到当前最小的未删数 p,再删掉 p 及其所有还没删掉的倍数。
cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, k;
cin >> n >> k;
vector<int> alive(n + 1, 1);
alive[0] = alive[1] = 0;
int cnt = 0;
while (true) {
int p = 0;
for (int i = 2; i <= n; i++) {
if (alive[i]) {
p = i;
break;
}
}
for (int x = p; x <= n; x += p) {
if (!alive[x]) continue;
alive[x] = 0;
cnt++;
if (cnt == k) {
cout << x << '\n';
return 0;
}
}
}
return 0;
}对本题来说,直接模拟已经足够。
实现时可以把外层写成 p = 2..n 顺序扫描:
- 如果
p已经被删掉,就跳过; - 如果还没删掉,就按步长
p枚举倍数并删数。
每当真正删掉一个新数,就把计数加一;当计数达到 k 时,这个数就是答案。
代码
cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, k;
cin >> n >> k;
vector<int> removed(n + 1, 0);
int cnt = 0;
for (int p = 2; p <= n; p++) {
if (removed[p]) continue;
for (int x = p; x <= n; x += p) {
if (removed[x]) continue;
removed[x] = 1;
cnt++;
if (cnt == k) {
cout << x << '\n';
return 0;
}
}
}
return 0;
}复杂度
本题规模很小,直接模拟完全足够。
空间复杂度是
总结
这题的关键不是求素数,而是严格按照筛法的“删数顺序”去模拟。