题意:给出起始年份st和结束年份ed,求给的日期,在这些年份中,分别是星期一~星期日几次。
思路:首先,根据泰勒公式算出1583.1.1是星期六(数据范围起始)。然后,由于闰年是400年一个循环,考虑到400年里,有97个闰年,不是闰年的年份有365天,365%7=1,也就是说不是闰年的时候每年的星期几的循环都推移一天,然后闰年还要多一天,也就是说要多400+97个推移量,497%7=0,也就是说400年是一个循环周期。那么只要先预处理出1583.1.1开始的400年里,题目给的这个日子,在每个星期x出现了几次,然后再把给的st和ed,算出有多少个400年循环,多出来的部分就找它对应的一开始的400年里哪一个即可。
代码:
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#include<iostream>
#include<map>
#include<vector>
#include<set>
#include<queue>
using namespace std;
int days[13] = { 0, 31, 28 ,31 ,30 ,31 ,30 ,31 ,31 ,30 ,31 ,30 ,31 };
int q, st, ed, day, month, gi[8], ans[404];
bool isleap(int year)
{
return (year % 400 == 0) || ((year % 4 == 0) && (year % 100));
}
int cal(int yy, int mm, int dd)
{
int sum = 0;
for (int i = 1; i < mm; i++)
{
sum += days[i];
if (isleap(yy) && i == 2)
sum++;
}
return sum+dd-1;
}
void cycle(int mm,int dd)
{
int cnt=1,now = 6;//1583/1/1是星期六
for (int i = 1583; i <= 1583 + 399; i++)
{
if (mm == 2 && dd == 29 && !isleap(i))
{
ans[cnt++] = 0;
}
else
{
ans[cnt++] = (cal(i, mm, dd) + now - 1) % 7 + 1;
}
if (isleap(i))
now = (now + 366 - 1) % 7 + 1;
else
now = (now + 365 - 1) % 7 + 1;
}
}
int main()
{
scanf("%d", &q);
while (q--)
{
memset(ans, 0, sizeof(ans));
memset(gi, 0, sizeof(gi));
scanf("%d %d %d %d", &st, &ed, &month, &day);
cycle(month, day);
int numed = (ed-1583 + 1) / 400;
int numst = (st - 1583) / 400;
for (int i = 1; i <= 400; i++)
{
gi[ans[i]] += (numed - numst);
}
int mped = ed - 1583 + 1 - numed * 400;
int mpst = st - 1583 - numst * 400;
for (int i = 1; i <= mpst; i++)
{
gi[ans[i]]--;
}
for (int i = 1; i <= mped; i++)
{
gi[ans[i]]++;
}
printf("%d", gi[7]);
for (int i = 1; i <= 6; i++)
{
printf(" %d", gi[i]);
}
puts("");
}
return 0;
}