专栏文章

题解:P7075 [CSP-S2020] 儒略日

P7075题解参与者 5已保存评论 4

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
4 条
当前快照
1 份
快照标识符
@miqlpbza
此快照首次捕获于
2025/12/04 06:51
3 个月前
此快照最后确认于
2025/12/04 06:51
3 个月前
查看原文
这篇题解本来有很多前言,但是说多了都是泪,还是不说了。

题意

给定 nn,请你求从公元前 4713 年 1 月 1 日往后数 nn 天的日期。
简明的题意,屎山的代码。

思路

发现公元 1582 年 10 月 4 日之后改了历法,那么这里就是一个转折点。为了防止出错,我选择将此之前和此之后分开处理,写成 solve1()solve2()
  • 儒略历处理
    400400 年有 146100146100 天。我的做法的流程如下:
    1. NN 为经过了多少个整 400400 年,即取 N=n146100N=\lfloor\frac{n}{146100}\rfloor,然后使天数 nn 减去 N×146100N\times 146100
    2. 取年份 y=4713+N×400y=-4713+N\times400
    3. 因为公元零年并不存在,所以若 y>1y>1,使 yy 自增 11
    4. 因为公元前 4713 年 1 月 1 日被记作第 00 天,我们又是把公元前 4713 年 1 月 1 日当做第 11 天数的,所以要把 nn 加上 11。注意这里不可以一开始就这样做,否则会 8080
    5. 处理不足 400400 年的整年。这一块可以枚举。从已经得到的 yy 开始,只要 n>366n>366,每次判断 yy 是否是闰年。若是,nn 每次减去 366366,否则每次减去 365365。每次 yy 增加 11
    6. 处理月。设 monthimonth_i 表示 ii 月有 monthimonth_i 天。先判断现在得到的年份 yy 是否为闰年,若是把 month2month_2 设为 2929,否则设为 2828。从一月开始,设月份 yue=1yue=1,只要 n>monthyuen>month_{yue}yueyue 每次增加 11nn 减去 monthyuemonth_{yue}
    7. 这时我们已经处理好了年和月,剩下的 nn 就是日期。但是还有很多细节等待我们处理。让我们一一处理这些细节。
      由于前面年是一个一个加的,所以有可能加到 00 年,把它变成 11 年就好了。
      因为月份是从 11 开始数的,所以有可能会数到 1313 月。需要把年份加 11,把月份变为 11
      输出时,要输出 y|y|,若 y<0y<0,不要忘了输出 BC
好的那么 solve1() 就大功告成了。
solve2() 只需要在 solve1() 的基础上改一些地方。
  1. 400400 年有 146097146097 天,所以取 N=n146097N=\lfloor\frac{n}{146097}\rfloor
  2. 我们是从公元 1582 年 10 月 15 日开始数的,所以要把 nn 减去 22991712299171,这是从公元前 4713 年 1 月 1 日到公元 1582 年 10 月 15 日的天数;把 yy 的初始值赋成 15821582,再把 nn 加上 287287,这是从 1 月 1 日到 10 月 15 日的天数。
  3. 免去了判断公元前和零年的判断,因为最少到 1582 年。
考虑主函数里怎么写。考虑到换历法删除的 1010 天,若该日期在公元 1582 年 10 月 15 日(含)以后,直接把 nn 加上 1010 即可。
按儒略历手算可以算出公元 1582 年 10 月 4 日的儒略日是 22991602299160。所以判断若 n2299160n\le2299160,按儒略日用 solve1() 处理,否则将 nn 加上 1010,用 solve2() 处理。
最后也是最重要的判断闰年。仔细读题可以发现,1582 年之前用的儒略历,只要年份是 44 的倍数就闰,否则就是我们熟悉的闰年法。对于公元前的年份 xx,应把它看做 x1-x-1 年。
到这就写完了,可是这些调了我 3 天。我的码风不怎么样,写了 70 行。

AC Code

CPP
#include<bits/stdc++.h>
#define int long long
using namespace std;
int T,x;
int month[15]= {29,31,28,31,30,31,30,31,31,30,31,30,31};
bool check(int x) {
	if(x<=1582) {//如果是儒略历判断闰年就是4年一闰 
		if(x<=0) x=-x-1;
		return x%4==0;
	}
	if(x%400==0||(x%4==0&&x%100!=0)) return 1;//格里高利历判断闰年 
	return 0;
}
void solve1(int x) {
	int N=x/146100;
	int y=-4713+N*400;
	if(y>1) y++;
	x-=N*146100;//处理整400年 
	x++;
	for(; x>366; y++) {//处理整年 
		int f=0;
		if(y==0) y++;
		f=check(y);
		if(f) x-=366;
		else x-=365;
	}
	int yue=1;
	int f=0;
	f=check(y);
	if(f) month[2]=29;//如果是闰年,把month[2]设成29 
	for(; yue<=12&&x>month[yue]; yue++) x-=month[yue];//处理月 
	if(y==0) y=1;
	if(yue==13) yue=1,y++;//处理细节 
	cout<<x<<' '<<yue<<' '<<abs(y);
	if(y<0) cout<<" BC";
	cout<<endl;
}
void solve2(int x) {
	x-=2299171;
	int y=1582,yue=1;
	x+=287;
	int N=x/146097;
	y+=N*400;
	x-=N*146097;
	x++;
	for(; x>366; y++) {
		int f=0;
		f=check(y);
		if(f) x-=366;
		else x-=365;
	}
	int f=0;
	f=check(y);
	if(f) month[2]=29;
	for(; yue<=12&&x>month[yue]; yue++) x-=month[yue];
	if(yue==13) yue=1,y++;
	cout<<x<<' '<<yue<<' '<<y;
	cout<<endl;
}
signed main() {
	cin>>T;
	while(T--) {
		cin>>x;
		month[2]=28;
		if(x>=2299161) x+=10;
		if(x<=2299160) solve1(x);//判断用solve1还是solve2 
		else solve2(x);
	}
	return 0;
}

评论

4 条评论,欢迎与作者交流。

正在加载评论...