usaco-2.2.1 序言页码

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

每个十进制数位的罗马写法唯一固定(含 IV、IX、XC 等减法组合),按千/百/十/个位四张表逐页拼接 1..N 的罗马表示并累计 7 个字符出现次数。

OJ: roj

题目 ID: 2026

难度:入门

标签:入门模拟查表python

创建: 2026-10-01 03:32

更新: 2026-10-06 09:46

题目描述

给定整数 NN(1⩽N<35001 \leqslant N < 3500)。把 1,2,…,N1,2,\dots,N 每个页码写成标准罗马数字,统计 I V X L C D M 各字符出现总次数,按该顺序每行输出 字符 次数,未出现的字符不输出。样例:输入 5,输出 I 7 与 V 2。

思路

标准罗马数字每个十进制数位的写法唯一且固定,减法组合只出现在 4 与 9 两列。用千、百、十、个位四张 0…9 片段表把每页逐位拼成罗马数字并累加字符计数,最后按序输出非零项。

参考代码

Python 版:

python
"""ROJ 2026 序言页码:统计 1..N 的罗马数字中每个字符出现次数。"""
import sys
from collections import Counter

# 各数位 0~9 对应的罗马片段(千/百/十/个位),每行即标准罗马记数法
DIGITS = [
    ["", "M", "MM", "MMM"],                                    # 千位:1000~3000
    ["", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"],  # 百位
    ["", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"],  # 十位
    ["", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"],  # 个位
]


def roman(n: int) -> str:
    """把 1..3499 的整数转成标准罗马数字。"""
    return DIGITS[0][n // 1000] + DIGITS[1][n // 100 % 10] + DIGITS[2][n // 10 % 10] + DIGITS[3][n % 10]


def main() -> None:
    n: int = int(sys.stdin.read())
    cnt: Counter[str] = Counter(ch for i in range(1, n + 1) for ch in roman(i))
    # 字符按数字表从小到大输出,跳过未出现的字符
    print("\n".join(f"{c} {cnt[c]}" for c in "IVXLCDM" if cnt[c]))


main()

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:46
 * update_at: 2026-10-06 09:46
 */
#include <iostream>
using namespace std;

typedef long long ll;

// 各数位 0~9 对应的罗马片段:digit[0] 千位,digit[1] 百位,digit[2] 十位,digit[3] 个位
const char *digit[4][10] = {
    {"", "M", "MM", "MMM", "", "", "", "", "", ""},
    {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"},
    {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"},
    {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"},
};

// 按数字表中从小到大的顺序输出:I V X L C D M
const char ROMAN_CHAR[7] = {'I', 'V', 'X', 'L', 'C', 'D', 'M'};

ll cnt[26]; // cnt[c - 'A'] 记录字符 c 在页码 1..N 的罗马表示中出现次数

// 把一页页码 n 的罗马表示逐字符计入计数器
void count_page(ll n) {
    int d[4]; // d[0] 千位,d[1] 百位,d[2] 十位,d[3] 个位
    d[0] = n / 1000;
    d[1] = n / 100 % 10;
    d[2] = n / 10 % 10;
    d[3] = n % 10;
    for (int p = 0; p < 4; p++) {
        const char *s = digit[p][d[p]];
        for (int k = 0; s[k] != '\0'; k++) {
            cnt[s[k] - 'A']++;
        }
    }
}

int main() {
    ll n;
    cin >> n;
    for (ll i = 1; i <= n; i++) {
        count_page(i);
    }
    // 按 I V X L C D M 顺序输出出现过的字符
    for (int k = 0; k < 7; k++) {
        int idx = ROMAN_CHAR[k] - 'A';
        if (cnt[idx] > 0) {
            cout << ROMAN_CHAR[k] << " " << cnt[idx] << "\n";
        }
    }
    return 0;
}