儒略曆解題報告
儒略日
從上次 csp 考場上下來之後,就一直對這題很有心理陰影。
然後想著今天補一補這個題,那麼一些經驗就是,不要太害怕他,不要總想著如何快速推一些東西,靜下心來一點點看,然後多定義一些打表的陣列,這個資料萬一用到了呢?多定義一些簡單函式,可以極大簡化程式碼內部的細節特判,全部扔到函式裡面判斷。
首先我們考慮把格里高利曆和儒略曆劃分開來,因為這個分界線是非常的明顯的,而且分界線前後計算閏年的方式不同。
這個時候不要對自己的手算過於自信,當然,這個手算很容易,但是作為一個信競生,也許我們應該更好的利用手邊的工具,況且,前期的一些暴力打表和小函式,對於這類大模擬是很有用的。
那麼我們首先把一些基礎的定義出來,比如說如何判斷閏年:
inline int gr(int x) {
if (!(x % 400)) return 1;
if ((x % 100) && !(x % 4)) return 1;
return 0;
}
inline int rr(int x) {
if (!(x % 4)) return 1;
return 0;
}
中英混合命名,第一個是 \(g\) 格里高利 \(r\) (run) 年,第二個同理。
然後再把月份有多少天定義出來:
for (int i = 1; i <= 12; ++i) { if (i == 2) d[i] = 28; else if (i == 1 || i == 3 || i == 5 || i == 7 || i == 8 || i == 10 || i == 12) d[i] = 31; else d[i] = 30; }
這個時候我們就可以很快的求出儒略曆到底多少天了:
int sd = 0;
for (int i = -4712; i <= 1581; ++i) {
sd += rr(i) ? 366 : 365;
}
for (int i = 1; i <= 9; ++i) {
sd += d[i];
}
sd += 3;
暴力列舉整年並處理,暴力列舉整月並處理,最後處理多餘的天,之所以這裡 \(+=3\),是因為我們是按照 \(day\) 來判斷的,而 \(day\) 表示的是往後走了多少天,所以這裡不包括最後一天因為最後一天不能往後走了。
那麼對於詢問給出的 \(day\)
if (day > 2299160)
就可以判斷會到達格里高利曆,否則不會。
好了,那麼接下來我們知道會走到什麼歷了,那麼對於某個歷怎麼計算呢?
注意到儒略曆四年是一個週期,而格里高利曆四百年是一個週期,於是我們可以打出一個欽定第一年為閏年的 400 年的表,具體來說,我們打出 \(ny[k], nm[k], nd[k]\) 表示從某個閏年 \(1\) 月 \(1\) 日開始走,會走到 400 年內的哪一年哪一月哪一日,這個處理也不難。
for (int i = 0, k = 0; i < 400; ++i) {
for (int j = 1; j <= 12; ++j) {
int dd = d[j];
if (j == 2 && gr(i)) ++dd;
for (int ds = 1; ds <= dd; ++ds) {
ny[k] = i, nm[k] = j, nm[k] = ds;
++k;
}
}
}
列舉年月日,搞一個變數記錄一下多少天了就行。
於是接下來就可以週期處理:
while (q--) {
i64 day;
std::cin >> day;
int t = 0;
if (day > 2299160) { // g 歷
day -= 2299161;
day += 139810;
// 手動修正到從 1200-1-1 開始走
// 因為我們的表是要求從某個閏年開始,特殊的,格里高利欽定是必須為 400 倍數的某個閏年,畢竟你看那個打表方式就是欽定這是第一個 400 閏年,後面都不是
t = 1200 + (day / 146097) * 400;
// 從 1200 開始走,看有多少個 400 年
day %= 146097; // 400 年 400 年的幹掉
}
else { // r 歷
t = -4712 + (day / 1461) * 4;
// 4 年 4 年的幹掉
day %= 1461;
}
if (t + ny[day] > 0) { // 如果年份 + 剩餘天數走的年份是 DC
std::cout << nd[day] << ' ' << nm[day] << ' ' << t + ny[day] << '\n';
}
else { // 否則是 BC 捏
std::cout << nd[day] << ' ' << nm[day] << ' ' << -(t + ny[day] - 1) << ' ' << "BC" << '\n';
}
}
至於那個修正是怎麼求的,第一步是先幹到格里高利第一天,然後往前修正,這個往前修正需要修正多少天也好求:
int sd = 0;
for (int i = 1200; i <= 1581; ++i)
sd += gr(i) ? 366 : 365;
for (int i = 1; i < 10; ++i)
sd += d[i];
sd += 14;
std::cout << sd << '\n';
那麼整體的程式碼如下:
#include <bits/stdc++.h>
using i64 = long long;
int d[13];
inline int gr(int x) {
if (!(x % 400)) return 1;
if ((x % 100) && !(x % 4)) return 1;
return 0;
}
inline int rr(int x) {
if (!(x % 4)) return 1;
return 0;
}
int ny[146097], nm[146097], nd[146097];
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
for (int i = 1; i <= 12; ++i) {
if (i == 2) d[i] = 28;
else if (i == 1 || i == 3 || i == 5 || i == 7 || i == 8 || i == 10 || i == 12) d[i] = 31;
else d[i] = 30;
}
for (int i = 0, k = 0; i < 400; ++i) {
for (int j = 1; j <= 12; ++j) {
int dd = d[j];
if (j == 2 && gr(i)) ++dd;
for (int ds = 1; ds <= dd; ++ds) {
ny[k] = i, nm[k] = j, nd[k] = ds;
++k;
}
}
}
int q;
std::cin >> q;
while (q--) {
i64 day;
std::cin >> day;
int t = 0;
if (day > 2299160) { // g
day -= 2299161;
day += 139810;
t = 1200 + (day / 146097) * 400;
day %= 146097;
}
else { // r
t = -4712 + (day / 1461) * 4;
day %= 1461;
}
if (t + ny[day] > 0) {
std::cout << nd[day] << ' ' << nm[day] << ' ' << t + ny[day] << '\n';
}
else {
std::cout << nd[day] << ' ' << nm[day] << ' ' << -(t + ny[day] - 1) << ' ' << "BC" << '\n';
}
}
}
我不想就這樣淪陷,迷失在黑夜,我將燃燒這生命,就算再壯烈。