题解 P7075 【儒略日(洛谷民间数据)】
萌新第一次写题解,有写得不好的地方请多指教~~
今年CSP,两点半看题,整个人都傻了:第一题就是大模拟?!
读了十分钟才明白题意,其实模拟起来不是很复杂,只是坑点很多...
题意理解:
建立时间轴,以公元前4713年1月1日为原点,给定q个坐标,求坐标对应的公元时间。
易错点:
1.公元零年不存在。很重要,在判闰年时有用。
2.闰年的判断:有三种判断方式:
1)公元前1,5,9,...4的倍数加一为闰年。
2)公元后且1582年前,4的倍数为闰年(没有公元零年!)。
3)1582年后,4的倍数且不是100的倍数,或400的倍数为闰年。
3.1582.10.5-10.14不存在。
4.输入输出注意,某日某月某年,公元前加BC。
如果直接大模拟的话,大概是读入坐标,然后一段一段直接模拟。但这样不仅拖慢速度,且码量巨大让人秃头。而本题特征明显:公元前,公元后且1582年前和公元1582年后是明显的三段,因此可以在读入的过程中先将日期分在某个时间段内,有效减少了代码量。
int y=-4713,d=1,m=1;//年月日初始化
if(n>=1721424)y=1,n-=1721424;// 1721424是公元前的天数,判断年份属于哪一段
判断闰年部分:
if(mon!=2)return 0;
if(year<0){//公元前的情况
year=-year-1;//取正后-1
if(year%4==0||year==0)return 1;
return 0;
}
else{
if(year<1582){//公元后,1582年前的情况
if(year%4==0)return 1;
return 0;
}
else{//1582年后
if((year%4==0&&year%100!=0)||year%400==0)return 1;
return 0;
}
}
} //这么写看着丑,而且麻烦,不过好在简单易懂
主函数:
int main(){
cin>>q;
while(q--){
bool flag=0;
scanf("%lld",&n);
int y=-4713,d=1,m=1;
if(n>=1721424)y=1,n-=1721424;
while(n>0){
if(d>month[m]+run(y,m))d=d-month[m]-run(y,m),m++;//日的进位
if(m>12)m=1,y++;//月的进位
if(n>=365+run(y,2)){//闰年则为366天
n=n-365-run(y,2);
y++;
continue;//注意这里,当n大于一年就不会增加月份和日期
}
if(n>month[m]+run(y,m)){//判断是否是闰月
n=n-month[m]-run(y,m);
m++;
continue;
}
else{
d+=n;//小于一月,直接加在天数上
n=0;
break;
}
}
if(y>1582)flag=1;
if(y==1582&&m>10)flag=1;
if(y==1582&&m==10&&d>4)flag=1;//若答案在1582.10.4之后,直接加上10天
if(flag)d+=10;
if(d>month[m]+run(y,m))d=d-month[m]-run(y,m),m++;//进位
if(m>12)m=1,y++;
if(y>0)printf("%d %d %d\n",d,m,y);//公元前还是公元后?输出
else printf("%d %d %d BC\n",d,m,-y);
}
}
优化:
当您看到这里的时候,这道题基本就做完了。
可我们看一下数据:q为1e5,答案年份不超过1e9...
不要慌,这代表我们需要一定的优化。
有两种优化方式:一种是读入所有的天数,离线排序后模拟。(因为太菜考场上没想到这个)
另一种是快速确认大概日期,再逐年模拟。
日期的确定和闰年有关。观察闰年的判定,我们发现:可以以400年为一个周期。
在1582年之前,每四年一个闰年,400年有146100天。
在1582年之后,400年有97个闰年,有146097天。
所以...
if(n>146097&&y>1582){
y=y+n/146097*400; //直接除加取模,跑得快~
n%=146097;
}
if(n>146100&&y+400<1582){ //保证四百年后也在1582年前
y+=400;
n-=146100;
}
这样跑得也不会飞快,但起码最慢300ms也能过了。
总结:
总的来说,这是一道模拟好题,让我学到了很多。
重看本题,理解了意义不明的题面之后,思路也不复杂(模拟),代码并不会太长(66行)。
但数不胜数的坑点,特殊的数据和离奇的程序输出让许多OIer亲切的询问出题人住址(雾
在考场大概用了二十分钟读题分析,写代码用了四十分钟左右,对拍了二十分钟(?)
思路清晰的话还是好解决的~
收获:1.一定要认真读题,分析题面,找可能的坑点!
2.合理安排程序执行顺序,减少冗杂操作。
3先写暴力,再加适宜且不影响正确性的优化。
4.一定注意数据范围!(没开long long扣了10分QAQ)