CSP2020 儒略日 題解


題目大意:求公元前 4713 年 1 月 1 日 經過 r 天后的日期,公元 1582 年 10 月 4 日以前適用儒略歷,公元 1582 年 10 月 15 日以后適用格里高利歷
 q 次詢問,\(q\leq 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\n",day,month,year);
	else printf("%d %d %d BC\n",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\n",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\n",v.d,v.m,-(v.y-1));
    }
    else{
	printf("%d %d %d\n",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;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM