• CSP2020 儒略日 题解


    题目大意:求公元前 4713 年 1 月 1 日 经过 r 天后的日期,公元 1582 年 10 月 4 日以前适用儒略历,公元 1582 年 10 月 15 日以后适用格里高利历
     q 次询问,(qleq 10^5)

    这题就我目前所知有三种做法:

    做法一

    大概就是先把儒略历和格里高利历的分界点判掉,然后两边分别先400年400年跳,100年100年跳,4年4年跳,1年1年跳,最后一个月一个月跳。
    这种做法挺难写的。。。考场上并没有去写这种做法。。。

    做法二

    我考场上的做法。
    一样的把分界点判掉,然后两边分开处理。
    我先把每一年都看成是366天,这样可以直接往后跳 r / 366 年,然后令 r %= 366 ,然后因为我把一些平年看成了闰年,所以我需要把r加上我跳过的这些年中的平年数量,这个可以简单前缀和相减得到。剩下的小于366天的部分暴力一天一天跳即可。
    这样做细节相对少一点,而且处理儒略历和格里高利历的方法类似,可以直接开两个namespace然后复制粘贴过去。
    然而蒟蒻考场上思路混乱这题还是写了1个多小时(不应该呀

    考场代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define N 1007
    #define M 1000007
    #define LL long long
    int Const=2299160; //r_i-=Const+1;
    namespace Julian{
        int type[N]={0,1,-1,1,0,1,0,1,1,0,1,0,1};
        int base=-4716,year,month,day;
    
        void reset(){
    	year=-4712,month=1,day=1;
        }
        int run(int x){
    	return x/4;
        }
        int pingnian(int l,int r){
    	l-=base,r-=base;
    	int rn=run(r)-run(l-1);
    	return (r-l+1)-rn;
        }
        inline void chkday(int x){
    	if(day>x)day=1,month++;
        }
        void travel(int times)
        {
    	while(times){
    	    day++; times--;
    	    if(month==2){
    		if(year%4==0)chkday(29);
    		else chkday(28);
    	    }
    	    else{
    		if(type[month]==1)chkday(31);
    		else chkday(30);
    	    }
    	    if(month==13)year++,month=1;
    	}
        }
        void solve(LL times){
    	reset();
    	while(times>=366){
    	    int dlt=times/366; times%=366;
    	    int ping=pingnian(year,year+dlt-1);
    	    year+=dlt,times+=ping;
    	}
    	travel(times);
    	if(year>0)printf("%d %d %d
    ",day,month,year);
    	else printf("%d %d %d BC
    ",day,month,-(year-1));
        }
    }
    namespace Gregorian
    {
        int type[N]={0,1,-1,1,0,1,0,1,1,0,1,0,1};
        int base=1580,year,month,day;
        void reset(){
    	year=1582,month=10,day=15;
        }
        int run(int x){
    	return x/4-x/100+x/400;
        }
        int pingnian(int l,int r){
    	//l-=base,r-=base;
    	int rn=run(r)-run(l-1);
    	return (r-l+1)-rn;
        }
        inline void chkday(int x){
    	if(day>x)day=1,month++;
        }
        void travel(int times)
        {
    	while(times){
    	    day++; times--;
    	    if(month==2){
    		if(year%400==0||(year%4==0&&year%100!=0))chkday(29);
    		else chkday(28);
    	    }
    	    else{
    		if(type[month]==1)chkday(31);
    		else chkday(30);
    	    }
    	    if(month==13)year++,month=1;
    	}
        }
        void solve(LL times){
    	reset();
    	while(times>=366){
    	    int dlt=times/366; times%=366;
    	    int ping=pingnian(year+1,year+dlt);
    	    year+=dlt,times+=ping;
    	}
    	travel(times);
    	printf("%d %d %d
    ",day,month,year);
        }
    }
    
    int main()
    {
        LL r;
        int m;
        scanf("%d",&m);
        for(int i=1;i<=m;i++){
    	scanf("%lld",&r);
    	if(r<=Const){
    	    Julian::solve(r);
    	}else{
    	    Gregorian::solve(r-(Const+1));
    	}
        }
        return 0;
    }
    

    做法三

    这种做法更显得无脑暴力,因为它将每400年组成的循环节中每一天的日期全部打表打出来了。
    首先注意到从公元前 4713 年 1 月 1 日 到公元 1582 年 10 月 4 日中间只有2299161天,我们可以把这2299161天的日期全部打表打出来,这样就可以直接回答所有儒略历的询问了。
    然后处理格里高利历,我们建立两个映射,一个是从400年中的某个日期映射到它是这个400年中的第几天,另一个是从400年中的第几天映射到它在这400年中的日期,然后直接把整400年的都跳掉,然后剩下的天数直接通过映射表查询它会对应到那个日期。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define LL long long
    const int N=3e6+7;
    const int M=2e5+7;
    struct date{
        int y,m,d;
    }ju[N],gr[M];
    int year,month,day;
    int ty[13]={0,1,-1,1,2,1,2,1,1,2,1,2,1};
    int tot,len,idx[407][13][33];
    inline void chkday(int x){
        if(day>x)day=1,month++;
        if(month>12)month=1,year++;
    }
    void init(){
        year=-4712,month=1,day=1;
        ju[0]={year,month,day};
        while(year!=1582||month!=10||day!=4){
    	day++; 
    	if(ty[month]==1)chkday(31);
    	else if(ty[month]==2)chkday(30);
    	else {
    	    if(year%4==0)chkday(29);
    	    else chkday(28);
    	}
    	ju[++tot]={year,month,day};
        }
    }
    
    void init_2(){
        year=0,month=1,day=1;
        while(year!=400){
    	idx[year][month][day]=len;
    	gr[len++]={year,month,day};
    	day++;
    	if(ty[month]==1)chkday(31);
    	else if(ty[month]==2)chkday(30);
    	else{
    	    if(year%400==0||(year%4==0&&year%100!=0))chkday(29);
    	    else chkday(28);
    	}
        }
    }
    void print(date v){
        if(v.y<=0){
    	printf("%d %d %d BC
    ",v.d,v.m,-(v.y-1));
        }
        else{
    	printf("%d %d %d
    ",v.d,v.m,v.y);
        }
    }
    void Gregor(LL times){
        year=1582,month=10,day=15;
        year+=(times/len)*400,times%=len;
        int rest=(year%400+400)%400;
        year-=rest;
        int ind=idx[rest][month][day];
        ind+=times;
        if(ind>=len)year+=400,ind-=len;
        year+=gr[ind].y,month=gr[ind].m,day=gr[ind].d;
        print({year,month,day});
    }
    int main()
    {
        init(); init_2();
        int q;
        scanf("%d",&q);
        for(int i=1;i<=q;i++){
    	LL r; scanf("%lld",&r);
    	if(r<=tot)print(ju[r]);
    	else Gregor(r-tot-1);
        }
        return 0;
    }
    
  • 相关阅读:
    mssql分页原理及效率分析
    [ActiveX]使用VS2010创建MFC ActiveX工程项目
    Study notes for Discrete Probability Distribution
    oracle存储过程异常捕获
    (Python学习9)Python虚拟机中的一般表达式
    用Arduino做一个可视化网络威胁级别指示器!
    iOS上线项目源码分享
    android实习程序6——拨号通话
    评价等级使用的五星选择,包含半星的选择
    AJAX实现无刷新验证用户名
  • 原文地址:https://www.cnblogs.com/lishuyu2003/p/13954520.html
Copyright © 2020-2023  润新知