1 条题解

  • 0
    @ 2026-10-27 1:47:48

    思路与算法

    用天数定位日期,避免从公元前逐日模拟。1582年10月15日以前使用儒略历,自天文年-4712起按1461天四年周期跳过;改革后先处理1582年剩余区间,再从1583年1月1日按146097天四百年周期跳过,最后至多扫描400年与12个月。内部用含0的天文纪年,输出公元前年份时换成1-y并加BC。

    正确性说明

    儒略历四年恰1461天,格里高利历每400年含97闰日,分别整周期跳跃不改变周期内日期。改革时1582年10月4日下一天是10月15日,对应连续儒略日2299160、2299161;单独区间保证不存在被跳过的日期。剩余逐年逐月减完整长度,停留位置的余数加1就是日号。最后纪年转换排除公历输出中的0年。

    复杂度

    每个询问O(400+12)时间、O(1)额外空间;含固定月份数组初始化,总O(Q*400)时间,询问流式处理。

    易错点

    公元前1年与公元1年相邻;改革缺10天,不能混用两种闰年规则。天数与1e9年份运算用64位;大年份不能逐年从起点扫描。

    参考代码(C++11)

    #include <bits/stdc++.h>
    using namespace std;
    
    bool leap(long long y, bool greg) {
        return y % 4 == 0 && (!greg || y % 100 != 0 || y % 400 == 0);
    }
    int main() {
        int Q;
        cin >> Q;
        while (Q--) {
            long long r;
            cin >> r;
            long long y;
            int month = 1;
            bool greg = r >= 2299161;
            if (!greg) {
                y = -4712 + 4 * (r / 1461);
                r %= 1461;
            } else if (r < 2299239) {
                y = 1582;
                month = 10;
                r = r - 2299161 + 14;
            } else {
                r -= 2299239;
                y = 1583 + 400 * (r / 146097);
                r %= 146097;
            }
            while (r >= 365 + leap(y, greg)) {
                r -= 365 + leap(y, greg);
                y++;
            }
            int days[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
            days[2] += leap(y, greg);
            while (r >= days[month]) {
                r -= days[month];
                month++;
            }
            cout << r + 1 << " " << month << " ";
            if (y <= 0)
                cout << 1 - y << " BC\n";
            else
                cout << y << "\n";
        }
    }
    
    • 1

    信息

    ID
    1972
    时间
    1000ms
    内存
    128MiB
    难度
    普及−
    标签
    递交数
    0
    已通过
    0
    上传者