按月递推,利用当月天数模 7 转移下月 13 号的星期,统计 n 年内各星期出现次数。
OJ: roj
题目 ID: 2002
难度:入门
标签:模拟日期与日历
创建: 2026-05-22 19:17
更新: 2026-10-06 09:05
题目描述
给定非负整数
1900 年 1 月 1 日为星期一;4、6、9、11 月 30 天,1、3、5、7、8、10、12 月 31 天;闰年 2 月 29 天,平年 28 天。闰年判定:能被 4 整除且不能被 100 整除,或能被 400 整除。
思路
1900 年 1 月 13 日是星期六。每个月 13 号到下个月 13 号恰好相隔当月总天数,因此星期直接加当月天数再模 7 即可递推。遍历
参考代码
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-05-22 19:17
# update_at: 2026-05-22 19:17
import sys
# 规则平年各月天数
MONTH_DAYS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
def is_leap(year: int) -> bool:
"""判断年份是否为闰年。4 的倍数且非 100 的倍数,或 400 的倍数。"""
return (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0)
def days_of_month(year: int, month: int) -> int:
"""返回给定年月的总天数(1-indexed)。"""
return 29 if month == 2 and is_leap(year) else MONTH_DAYS[month - 1]
def count_thirteens(n: int) -> list[int]:
"""统计从 1900 年开始的 n 年中,每月 13 号落在星期六、日、一至五的次数。"""
# 0 代表星期六,1 代表星期日,...,6 代表星期五
# 1900-01-01 是星期一,经过 12 天到 1900-01-13 为星期六,故初始星期为 0 (星期六)
counts = [0] * 7
weekday = 0
for year in range(1900, 1900 + n):
for month in range(1, 13):
counts[weekday] += 1
weekday = (weekday + days_of_month(year, month)) % 7
return counts
def solve() -> None:
data = iter(map(int, sys.stdin.buffer.read().split()))
n = next(data)
counts = count_thirteens(n)
print(" ".join(map(str, counts)))
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:05
* update_at: 2026-10-06 09:05
*/
#include <iostream>
using namespace std;
typedef long long ll;
int mdays[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
// 判断闰年:4 的倍数且非 100 的倍数,或 400 的倍数
bool is_leap(int year) {
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}
// 返回 year 年 month 月的总天数(month 为 1~12)
int days_of_month(int year, int month) {
if (month == 2 && is_leap(year)) return 29;
return mdays[month];
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
if (!(cin >> n)) return 0;
// cnt[w] 表示 13 号落在星期 w 的次数
// w 定义:0=星期六,1=星期日,2=星期一,3=星期二,4=星期三,5=星期四,6=星期五
int cnt[7] = {0};
// 1900-01-01 是星期一,到 1900-01-13 相差 12 天,(2+12)%7=0,即星期六
int weekday = 0;
for (int year = 1900; year <= 1900 + n - 1; ++year) {
for (int month = 1; month <= 12; ++month) {
cnt[weekday]++;
weekday = (weekday + days_of_month(year, month)) % 7;
}
}
for (int i = 0; i < 7; ++i) {
if (i) cout << ' ';
cout << cnt[i];
}
cout << '\n';
return 0;
}