区间内的真素数 的题解
在解题之前提交题解的代码会导致封禁。
作者:
概述
本题要求找出闭区间 内的所有真素数,即自身与其十进制反序均为素数的正整数,按数值从小到大用逗号连接输出,区间内不存在这样的数时输出
No。核心解法是直接枚举区间内的每个整数,用算术循环求出它的十进制反序,再对这两个数各做一次试除法素性判定。
分析
核心观察
判断一个正整数 是否为真素数需要两次素性测试:
是素数,且
的十进制反序
也是素数,两次判定必须同时成立。由于
,
至多为
(
的反序是
),两个数的量级都在
以内,试除法在两种情况下代价相当,不需要为反序单独做特殊处理。
另一个关键点是反转必须按数值理解:末尾的 反转后成为前导零并被丢弃,例如
的反序是
,所以
不是真素数。
思路
最直接的做法就是枚举:对区间内的每个整数 ,用算术循环求出它的反序
,再用试除法分别判断
与
是否为素数,两者都是就输出
。反序用数值运算而不是字符串反转,末尾的
会被自然丢弃,与题面「反序」的数值含义天然一致;若改用
to_string 加 reverse,还需要一次 stoi 或 int 转换才能得到同样的数值。
试除法的单次素性判定上界是 ,于是枚举的复杂度上界为
,看起来最坏达到
次取模。实际上这个上界只在判定对象是素数时才会取到:试除在找到第一个因子时立即退出,偶数一次就能排除,
的倍数两次就能排除,只有
以内的
个素数会走满
。因此真实运算量约为
量级,本机实测最坏测试点(
、
)耗时在 C++ 与 Python 下都远低于
秒。
如果想进一步加速,可以先把 以内的素数表用埃氏筛预处理出来,此后每次素性判定降为
查表;但本题的数据规模并不需要这一步,直接枚举的写法更短、也不引入筛法的边界(
与
必须手动置为非素数)。
枚举按 递增进行,输出天然有序,无需排序。无解时题面要求输出
No,用一个布尔标志记录是否已经输出过至少一个数即可:枚举结束后标志仍为假就输出 No,否则依次输出逗号与数字。
具体示例
以样例 、
为例,区间内的素数为
,逐个求反序并判定:
| 反序 |
是否输出 | ||
|---|---|---|---|
| 是 | 是 | ||
| 是 | 是 | ||
| 是 | 是 | ||
| 否 | 否 | ||
| 否 | 否 | ||
| 否 | 否 | ||
| 是 | 是 |
按递增顺序拼接得到 11,13,17,31,与样例输出一致。
算法步骤
- 读入
M与N。 - 从
i = M到i = N依次枚举:令x等于i、r等于,循环执行
r = r * 10 + x % 10与x //= 10,直到x为,此时
r就是i的反序。 - 用试除法判断
i是否为素数:i小于时直接判否,否则先排除偶数,再从
开始以步长
试除到
。
- 若
i是素数,用同样的方法判断r是否为素数;两者都是素数时把i追加到答案缓冲,并在追加前处理逗号分隔。 - 枚举结束后:答案缓冲为空则输出
No,否则输出拼接好的答案。 - 输出末尾补一个换行。
复杂度分析
- 时间:枚举
个数,每个数求反序需要
(
时至多
位数字),两次试除法素性判定的上界为
,故最坏情况为
。实际运算量远小于该上界,因为试除在命中小因子时立即退出。
- 空间:只需常数个辅助变量与答案缓冲,为
(缓冲为输出本身)。
实现注意事项
- 小于
的数不是素数:
1必须判否,否则1以及反序为1的数(如10、100)会被误判为真素数。 - 反序按数值处理:
的反序写作
001,其数值为,故
不是真素数;算术反转天然丢弃末尾的
。
- 反序的值域不会超过原来的上界(
内最大的反序为
),不需要额外的越界判断。
- 先判
i再判r:合数占绝大多数,短路求值能让第二次素性判定只在必要时执行(Python 的and与 C++ 的&&都短路)。 - 试除从
开始、步长为
,跳过偶数可省掉一半试除次数;
i为时要单独返回真。
- 逗号只加在相邻两项之间,末尾不能有多余逗号:用一个标志判断当前项是不是第一项。
- 空答案的标志同时用作
No的判定依据,No只能在枚举全部结束后输出,不能在前缀暂时为空时提前输出。 - 题目保证
,输入的两个数不需要交换。
No是完整的输出内容(首字母大写、不含其他字符),后面只跟一个换行。- 输出用缓冲拼接后一次写出:C++ 用
std::string累加再fwrite,Python 用join,比逐项输出更快。 - Python 读入用
sys.stdin.buffer.read().split()一次取完即可。
源代码
#include <cstdio>
#include <iostream>
#include <string>
namespace {
bool isPrime(int n) {
if (n < 2) return false; // 0 与 1 都不是素数
if (n % 2 == 0) return n == 2; // 2 是唯一的偶素数
for (long long d = 3; d * d <= n; d += 2) {
if (n % d == 0) return false;
}
return true;
}
// 十进制反序:末尾的 0 在反转后自动消失(100 → 1)
int reverseDecimal(int n) {
int r = 0;
while (n > 0) {
r = r * 10 + n % 10;
n /= 10;
}
return r;
}
} // namespace
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int m = 0, n = 0;
if (!(std::cin >> m >> n)) return 0;
std::string out;
bool first = true;
for (int i = m; i <= n; ++i) {
if (!isPrime(i)) continue; // 先判自身,短路掉大部分数
const int r = reverseDecimal(i);
if (!isPrime(r)) continue;
if (!first) out += ',';
out += std::to_string(i);
first = false;
}
if (first) out += "No";
out += '\n';
std::fwrite(out.data(), 1, out.size(), stdout);
return 0;
}
import sys
def is_prime(n):
if n < 2:
return False # 0 与 1 都不是素数
if n % 2 == 0:
return n == 2 # 2 是唯一的偶素数
d = 3
while d * d <= n:
if n % d == 0:
return False
d += 2
return True
def reverse_decimal(n):
r = 0
while n > 0: # 末尾的 0 在反转后自动消失(100 → 1)
r = r * 10 + n % 10
n //= 10
return r
def main():
data = sys.stdin.buffer.read().split()
if len(data) < 2:
return
m, n = int(data[0]), int(data[1])
res = [str(v) for v in range(m, n + 1)
if is_prime(v) and is_prime(reverse_decimal(v))]
sys.stdout.write(",".join(res) if res else "No")
sys.stdout.write("\n")
if __name__ == "__main__":
main()
评论