题意
传送门 POJ 1082
题解
设日历上
(
y
,
m
,
d
)
(y,m,d)
(y,m,d) 的下一个日期为
n
x
t
1
(
y
,
m
,
d
)
nxt1(y,m, d)
nxt1(y,m,d),下一个月的同一个日子的日期为
n
x
t
2
(
y
,
m
,
d
)
nxt2(y,m,d)
nxt2(y,m,d)。
d
p
[
(
y
,
m
,
d
)
]
dp[(y,m,d)]
dp[(y,m,d)] 代表日期
(
y
,
m
,
d
)
(y,m,d)
(y,m,d) 亚当先手是否可以取胜,考虑胜负态的转移
d
p
[
(
y
,
m
,
d
)
]
∣
=
!
d
p
[
n
x
t
1
(
y
,
m
,
d
)
]
∣
∣
!
d
p
[
n
x
t
2
(
y
,
m
,
d
)
]
dp[(y,m,d)]\ |=\ !dp[nxt1(y,m,d)] \ || \ !dp[nxt2(y,m,d)]
dp[(y,m,d)] ∣= !dp[nxt1(y,m,d)] ∣∣ !dp[nxt2(y,m,d)] 封装日期类并使之有序,可以通过
O
(
l
o
g
(
d
a
y
s
)
)
O(log(days))
O(log(days)) 的二分搜索简单地求某个日期的下一天以及判断某个日期的合法性。
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 400000
struct date
{
int y, m, d;
date() {}
date(int y, int m, int d) : y(y), m(m), d(d) {}
bool operator<(const date &b) const
{
if (y != b.y)
return y < b.y;
if (m != b.m)
return m < b.m;
return d < b.d;
}
} ds[maxn];
const int days[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int D;
bool win[maxn];
inline bool leap_year(int y)
{
return y % 400 == 0 || (y % 4 == 0 && y % 100 != 0);
}
void init()
{
for (int y = 1900; y <= 2001; ++y)
for (int m = 1; m <= 12; ++m)
for (int d = 1, ub = days[m] + (m == 2 && leap_year(y) ? 1 : 0); d <= ub; ++d)
ds[D++] = date(y, m, d);
sort(ds, ds + D);
int end = lower_bound(ds, ds + D, date(2001, 11, 4)) - ds;
for (int i = end + 1; i < D; ++i)
win[i] = 1;
for (int i = end - 1; i >= 0; --i)
{
if (!win[i + 1])
win[i] = 1;
else
{
date t = ds[i];
if (++t.m == 13)
++t.y, t.m = 1;
if (binary_search(ds, ds + D, t))
win[i] |= !win[lower_bound(ds, ds + D, t) - ds];
}
}
}
int main()
{
init();
int t, y, m, d;
scanf("%d", &t);
while (t--)
{
scanf("%d%d%d", &y, &m, &d);
puts(win[lower_bound(ds, ds + D, date(y, m, d)) - ds] ? "YES" : "NO");
}
return 0;
}