[CSP-S2020]儒略日 题解
题意分析
给出距离 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 一年