首页 > 技术文章 > [CSP-S2020]儒略日 题解

TEoS 2020-11-10 21:14 原文

题意分析

给出距离 4713.1.1 B.C 的天数,要求计算出具体的日期

思路分析

显然,模拟即可。考虑根据历法的分为不同的阶段进行计算,然后从大周期到小周期进行处理。

可以将最特殊的 1582 年单独分为一段,它之前的分为一段,它之后的分为一段。

对于第一段(1581-),最大的周期即为四年(四年一闰),因此以四年为周期划分;对于每个四年周期内,以一年为周期划分;对于每个一年周期内,以一月为周期划分;然后处理天数即可。由于 4713 B.C 刚好为闰年,因此从头开始划分即可。注意公元前和公元后的判断。

对于第二段(1582),按月划分,特判 10 月的 10 天缺失即可。

对于第三段(1583+),最大的周期为四百年(世纪年四次一闰),接下来为一百年,然后为四年、一年、一月,依次划分即可。因为最开始不满一个周期,因此可以接着划分成 1583 , 1584-1599 ,1600+ 三段,第一段按一年的最大周期处理,第二段按四年的最大周期处理,第三段按四百年的最大周期处理即可。注意世纪年的特殊闰年处理。

细节比较多,注意不同阶段和周期对应的数值。

考场代码,有些地方可能比较乱,见谅。

//FJ-00445
//NOIP2020 RP++
#include<iostream>
#include<cstdio>
#define ll long long
using namespace std;
int T;
int mp[12]={31,28,31,30,31,30,31,31,30,31,30,31};//平年的一月周期
int mr[12]={31,29,31,30,31,30,31,31,30,31,30,31};//闰年的一月周期
int sp[12]={31,28,31,30,31,30,31,31,30,21,30,31};//1582的一月周期
int yp[4]={366,365,365,365};//一年周期
ll n,ans;
void solve1()//1581-
{
    ans=n/1461*4;n%=1461;ll p,q;//四年周期
    for(p=0;p<4 && n>=yp[p];p++)
        n-=yp[p],ans++;//一年周期
    if(!p)//第一年为闰年
        for(q=0;q<12 && n>=mr[q];q++)
            n-=mr[q];//闰年的一月周期
    else
        for(q=0;q<12 && n>=mp[q];q++)
            n-=mp[q];//平年的一月周期
    if(ans<4713)
        printf("%lld %lld %lld BC\n",n+1,q+1,4713-ans);//公元前
    else
        printf("%lld %lld %lld\n",n+1,q+1,ans-4713+1);//公元后
}
void solve2()//1583+
{
    n-=2299239;ans=1583;ll p,q;//快进到1583
    if(n<365)//1583 
        for(q=0;q<12 && n>=mp[q];q++)
            n-=mp[q];//平年的一月周期
    else
        if(n<6209)//1584-1599
        {
            n-=365;ans++;//快进到1584
            ans+=n/1461*4;n%=1461;
            for(p=0;p<4 && n>=yp[p];p++)
                n-=yp[p],ans++;
            if(!p)
                for(q=0;q<12 && n>=mr[q];q++)
                    n-=mr[q];
            else
                for(q=0;q<12 && n>=mp[q];q++)
                    n-=mp[q];
        }
        else//1600+
        {
            n-=6209;ans=1600;//快进到1600
            ans+=n/146097*400;n%=146097;//四百年周期
            if(n<366)//只有第一个世纪年为闰年
                for(q=0;q<12 && n>=mr[q];q++)
                    n-=mr[q];
            else/
            {
                n--;//假装是第一个世纪年是平年,按照一般的一百年周期计算
                ans+=n/36524*100;n%=36524;//一百年周期
                if(n<365)//其余的世纪年均为平年
                    for(q=0;q<12 && n>=mp[q];q++)
                        n-=mp[q];
                else
                {
                    n++;//假装世纪年为闰年,按照一般的四年周期计算
                    ans+=n/1461*4;n%=1461;
                    for(p=0;p<4 && n>=yp[p];p++)
                        n-=yp[p],ans++;
                    if(!p)
                        for(q=0;q<12 && n>=mr[q];q++)
                            n-=mr[q];
                    else
                        for(q=0;q<12 && n>=mp[q];q++)
                            n-=mp[q];
                }
            }
        }
    printf("%lld %lld %lld\n",n+1,q+1,ans);
}
void solve3()//1582
{
    n-=2298884;ans=1582;ll q;//快进到1582
    for(q=0;q<12 && n>=sp[q];q++)
        n-=sp[q]; 
    if(q==9 && n>3)//10月特判
        n+=10;
    printf("%lld %lld %lld\n",n+1,q+1,ans);
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%lld",&n);
        if(n<2298884)
            solve1();
        else
            if(n>2299238)
                solve2();
            else
                solve3();
    }
    return 0;
}

下面给出一些数值的计算式:

  • 四年周期: $366+365+365+365=1461$ ,即一个闰年加三个平年
  • 一百年周期: $1461×\frac{100}{4}-1=36524$ ,即 $25$ 个四年周期减掉平年世纪年
  • 四百年周期: $36524×\frac{400}{100}+1=146097$ ,即 $4$ 个一百年周期加上闰年世纪年
  • 快进到 1582 :$1461× \left \lfloor \frac{4713+1581}{4} \right \rfloor +366+365= 2298884$ ,即 4713 B.C - 1581 A.D 的所有完整的四年周期加上 1580,1581 两年
  • 快进到 1583:$2298884+355=2299239$ ,即在 1582 年的基础上加上 1582 一年
  • 快进到 1600:$1461×\frac{1600-1584}{4}+365=6209$ ,即 1584-1599 四个完整的四年周期加上 1583 一年

 

推荐阅读