题解 P7075 儒略日

ShuYuMo

2020-11-13 17:10:11

Personal

题目看似很多细节,但是没必要特殊考虑,特殊考虑可能需要浪费很多时间。 考虑如果从头开始一天一天的往后跳,假如跳 $10^7$ 天,如果能预处理得到 $10^7$ 内步的日期,如果询问 $10^7$ 天内的日期,可以 $O(1)$ 回答。 先暴力一天一天的往后跳,预处理出前 $10^7$ 天的日期,年份能到达 $22666$ 年,所有的特殊情况都可以通过预处理的方式算出来。 对于询问分成两类: - 对于 $r_i \le 10^7$ 直接输出预处理好的日期 - 对于 $r_i > 10^7$ 二分年份是哪一年,这里的日期一定是**格里高利历**,判断闰年只有一种条件,相比于直接二分所有可能的年份,要好写很多很多,基本没有什么细节了。 代码算是比较短的了。 ```cpp #include <cstdio> #include <bits/stdc++.h> #define int long long using namespace std; const int _ = 1e7 + 100; const int LIM = 22666; int Day[2][30] = { {-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {-1, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} }; bool pd(int Y){ if(Y < 0) return Y % 4 == -1; else if(Y <= 1582) return Y % 4 == 0; else return (Y % 400 == 0) || (Y % 4 == 0 && Y % 100 != 0); } int CNTR(int Y){ //求 年份在[1, Y]之间,如果全部按照格里高利日历的闰年判断方法,得到的闰年数量。 int ans = 0; ans += Y / 400; ans += (Y / 4 - Y / 100); return ans; } void next(int &Y, int &M, int &D){ // 往下增加一天,得到的日期。 if(Y <= -1){ D++; int md = Day[pd(Y)][M]; if(D > md) D = 1, M ++; if(M > 12) M = 1, Y ++; if(Y >= 0) Y = 1; } else { D += (Y == 1582 && M == 10 && D == 4 ? 11 : 1); int md = Day[pd(Y)][M]; if(D > md) D = 1, M ++; if(M > 12) M = 1, Y ++; } } struct Date{ int D, Y, M; Date(int a, int b, int c) { Y = a; M = b; D = c; } Date(){} }A[_]; int check(int Y){ // 求出[LIM, Y]年之间有多少天。 int ALL_Y = Y - LIM; // 总共有多少年 int D = ((CNTR(Y) - CNTR(LIM))) * 366; // 闰年的数量 * 366 D += (ALL_Y - (CNTR(Y) - CNTR(LIM))) * 365; // 平年的数量 * 365 // 这里是一个前缀和,求出 [1, Y] 之间的闰年数量 - [1, LIM] 之间的闰年数量 就是[LIM, Y] 之间的闰年数量。 return D; } signed main(){ ios::sync_with_stdio(false); int Y = -4713, M = 1, D = 1; for(int i = 0; i <= 1e7 + 20; i++){ A[i] = Date(Y, M, D); next(Y, M, D); } // 预处理 int Q; cin >> Q; while(Q--){ int R; cin >> R; if(R <= (int)(1e7 + 11)) { printf("%lld %lld %lld", A[R].D, A[R].M, abs(A[R].Y)); if(A[R].Y < 0) puts(" BC"); else puts(""); } else { int res = R - (int)(1e7) - 11; int l = LIM, r = 2e9, Y = l; while(l < r){ int mid = (l + r) >> 1; if(check(mid) >= res) r = mid, Y = mid; else l = mid + 1; } res -= check(Y - 1); int M = 1; for(int i = 1; i <= 12; i++) { int md = Day[pd(Y)][i]; if(res - md <= 0) { M = i; break; } else res -= md; } int D = res; printf("%lld %lld %lld\n", D, M, Y); } } return 0; } ```