1 条题解
-
0
思路与算法
用天数定位日期,避免从公元前逐日模拟。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