usaco-1.5.3 特殊的质数肋骨

GitHub跳转原题关系图返回列表

由 {2,3,5,7} 逐位接 {1,3,7,9} 生长,每次试除判素,保留仍是质数的前缀。

OJ: roj

题目 ID: 2019

难度:普及-

标签:数论素数搜索usacopython

创建: 2026-10-01 03:18

更新: 2026-10-06 09:41

题目描述

给定 N(1 ≤ N ≤ 8),求所有长度为 N 的“特殊质数”:从最高位开始切下任意前缀(含完整数本身)都必须是质数。按升序每行输出一个。

思路

一位数特殊质数只有 {2,3,5,7}。长度大于 1 时,末位只能是 {1,3,7,9},否则会被 2 或 5 整除。逐位生长:对当前每个合法前缀,尝试接一位并试除判素,保留仍是质数的候选。由于按前缀升序、末位升序扩展,输出天然有序。

参考代码

Python 版:

python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-10-01 03:18
# update_at: 2026-10-01 03:18

import sys

# 每一位只可能是这 4 个数字:偶数与 5 会让末位组成的数被 2 或 5 整除
TAIL_DIGITS = (1, 3, 7, 9)
# 首位从这 4 个里取:1 不是质数,2、3、5、7 都是一位质数
HEAD_DIGITS = (2, 3, 5, 7)


def is_prime(x: int) -> bool:
    """判断 x 是否为质数(试除到 sqrt(x))。"""
    if x < 2:
        return False
    d = 2
    while d * d <= x:                # 只需试除到 sqrt(x):合数必有一个不超过 sqrt(x) 的因子
        if x % d == 0:
            return False
        d += 1
    return True


def solve() -> None:
    data = iter(map(int, sys.stdin.buffer.read().split()))
    n = next(data)

    # 逐位生长:prefix 是已经保证为特殊质数的前缀,往右再接一位再判定
    cur = [p for p in HEAD_DIGITS if is_prime(p)]   # 长度 1 的特殊质数
    for _ in range(n - 1):
        nxt: list[int] = []
        for prefix in cur:
            for d in TAIL_DIGITS:
                x = prefix * 10 + d
                if is_prime(x):                      # 新数仍是质数才留下,往后继续接
                    nxt.append(x)
        cur = nxt
        if not cur:                                  # 这一长度已经没有候选,再长更不可能有
            break

    sys.stdout.write('\n'.join(map(str, cur)) + ('\n' if cur else ''))


if __name__ == "__main__":
    solve()

C++ 版(同一算法):

cpp
/**
 * Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
 * rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
 * rainboy的学习导航网站: https://idx.roj.ac.cn
 * create_at: 2026-10-06 09:41
 * update_at: 2026-10-06 09:41
 */
#include <iostream>
using namespace std;

typedef long long ll;

ll cur[20]; // 当前长度的特殊质数
ll nxt[40]; // 下一长度的候选
int cur_cnt, nxt_cnt;

// 试除判素:合数必有一个不超过 sqrt(x) 的因子
bool is_prime(ll x) {
    if (x < 2) return false;
    for (ll d = 2; d * d <= x; ++d)
        if (x % d == 0) return false;
    return true;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);

    int n;
    if (!(cin >> n)) return 0;

    // 长度 1 的特殊质数,1 不算质数
    cur[0] = 2; cur[1] = 3; cur[2] = 5; cur[3] = 7;
    cur_cnt = 4;

    // 逐位生长:由长度 k 的特殊质数,末位接 {1,3,7,9} 并判素,得到长度 k+1
    for (int k = 1; k < n; ++k) {
        nxt_cnt = 0;
        for (int i = 0; i < cur_cnt; ++i) {
            for (int d = 0; d < 4; ++d) {
                ll tail = (d == 0) ? 1 : (d == 1) ? 3 : (d == 2) ? 7 : 9;
                ll x = cur[i] * 10 + tail;
                if (is_prime(x)) nxt[nxt_cnt++] = x;
            }
        }
        // 把 nxt 复制回 cur
        cur_cnt = nxt_cnt;
        for (int i = 0; i < cur_cnt; ++i) cur[i] = nxt[i];
    }

    for (int i = 0; i < cur_cnt; ++i) cout << cur[i] << '\n';
    return 0;
}