LG7075 [CSP-J2020] 儒略日

发布于 2020-11-08  3.65k 次阅读


题面

分析

听说可以用二分做,但是我直接模拟。

最开始我先直接把所有的日子直接转换成儒略历。

若转换后没有超过公元年,那么直接输出,结束

若超过公元年,先给年号 $+1$ (跳过 $0$ 年),然后再判断:

若没有超过 1582.10.5,直接输出。

否则,给天数加上十天,然后开始补格里高利历与儒略历之间的差距:每 400 年 3 天的差距。

补完之后输出就可以了。

注意你需要对这个做法进行加速,不然会 TLE 10 pts。

细节在代码里有。

代码

比较丑陋。考场上炸了 90 pts,因为第一次膜 $1461$ 时没有特殊处理导致 day 直接等于 $0$,直接爆炸。后来被官方数据又 hack 一个点,是中间的地方膜 $146097$ 时没有特殊处理的原因。

#include <bits/stdc++.h>
using namespace std;

template <typename T>
T read(void) {
    T f = 1, num = 0;
    char c = getchar();
    while (c < '0' || c > '9') {
        if (c == '-') f = -f;
        c = getchar();
    }
    while (c >= '0' && c <= '9') {
        num = (num << 3) + (num << 1) + (c ^ 48);
        c = getchar();
    }
    return f * num;
}

const int days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

inline int daysBefore(int year, int month) {  //儒略历天数
    if (month != 2) return days[month - 1];
    if ((year > 0 && year % 4 == 0) || (year < 0 && year % 4 == -1)) return 29;
    return 28;
}
inline int daysNear(int year, int month) {  //格里高利历天数
    if (month != 2) return days[month - 1];
    if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) return 29;
    return 28;
}
inline int daysInYear(int year) {  //格里高利历每年天数
    if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) return 366;
    return 365;
}

int main() {
    int Q = read<int>();
    while (Q--) {
        long long day = read<long long>();
        int year = -4713 + day / 1461 * 4, month = 1;
        //4年一个循环,4年天数共365*4+1=1461
        day %= 1461;
        day++;  //注意开始的时候是一月一号,所以要加一
        //公元年特判
        if (year >= 0) year++;
        //暴力弥补剩下的月份
        while (day > daysBefore(year, month)) {
            day -= daysBefore(year, month);
            if (++month == 13) month = 1, year++;
            if (year == 0) year++;
        }
        if (year < 0)  //公元前
            printf("%lld %d %d BC\n", day, month, -year);
        else {
            if (year > 1582 || (year == 1582 && month > 10) || (year == 1582 && month == 10 && day > 4)) {
                //如果超过了1582.10.4
                day += 10;  //补上十天
                //暴力处理月份
                if (daysBefore(year, month) < day) {
                    day -= daysBefore(year, month);
                    if (++month == 13) month = 1, year++;
                }
                //每400年补上3天
                long long plus = (year - 1600) / 400 * 3;
                //暴力枚举不足400年的剩余部分
                for (register int i = (year - 1600) / 400 * 400 + 1600; i < year || (i == year && month > 2); i += 100)
                    if (i % 400 != 0) plus++;
                //加上
                day += plus;
                //先将没有满的年份补满
                while (daysNear(year, month) < day) {
                    day -= daysNear(year, month);
                    if (++month == 13) {
                        month = 1, year++;
                        break;
                    }
                }
                //每400年一个轮回,400*365+100-4+1=146097天
                day--, year += day / 146097 * 400, day %= 146097, day++;
                while (daysInYear(year) < day) day -= daysInYear(year++);  //对于多余的年份加速处理
                //处理剩余出的月份
                while (daysNear(year, month) < day) {
                    day -= daysNear(year, month);
                    if (++month == 13) month = 1, year++;
                }
            }
            printf("%lld %d %d\n", day, month, year);
        }
    }
    return 0;
}

我缓慢吐出一串啊吧啊吧并不再想说话