黑色星期五

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

按月递推,利用当月天数模 7 转移下月 13 号的星期,统计 n 年内各星期出现次数。

OJ: roj

题目 ID: 2002

难度:入门

标签:模拟日期与日历

创建: 2026-05-22 19:17

更新: 2026-10-06 09:05

题目描述

给定非负整数 nn(n⩽400n \leqslant 400),统计从 1900 年 1 月 1 日到 1900+n−11900+n-1 年 12 月 31 日之间,每月 13 号落在星期六、星期日、星期一、星期二、星期三、星期四、星期五的次数,按该顺序输出 7 个整数。

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 即可递推。遍历 n×12n \times 12 个月统计次数。

参考代码

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;
}