「NOIP模擬賽」日曆遊戲
「NOIP模擬賽」日曆遊戲
「問題描述」
moreD和moreD的寵物CD正在玩一個日曆遊戲,開始時,他們從1900年1月1日到2012年12月22日(你懂的……)選一個日期開始,依次按照如下規則之一向後跳日期:
- 跳到日曆上的下一天。
- 跳到日曆上的下個月的同一天(如果不存在,則不能這麼做)。
要是誰正好到達2012年12月22日那麼他就贏了,如果到達這天之後的日期那他就輸了——原因你也懂的。
每次都是moreD先走的。
現在,給你一個日期,請問moreD一定能贏嗎?
「輸入」
輸入共T行,每行三個整數,Y、M、D,分別表示年、月、日。日期在1900年1月1日到2012年12月22日之間(包含兩端)。
T並不在輸入資料當中。
「輸出」
要是moreD一定能贏,輸出一行YES,否則輸出NO。
「輸入輸出樣例一」
2012 12 20
NO
「輸入輸出樣例二」
2012 12 21
YES
「資料描述」
對於50%的資料,是1949年1月1日後的日期。 T <= 5
對於100%的資料,是1900年1月1日後的日期。T <= 10
Solution
%你賽\(T1\)遇到這種毒瘤題。。也沒學過博弈論,於是自己玩了玩,整到了80pts,還以為是正確性有問題,沒想到是一個小細節的鍋。
接下來講講我的思路。
首先,如果這兩個人都天資聰慧,兩種操作都會用,我這種蒟蒻不會判斷啊。。
所以我們假設他們都只會往後跳一天,也就是禁用操作二。
很顯然,在這樣的限制下,用結束日期減去給定日期,如果是奇數天,那麼我方必勝,否則必敗。
那麼操作二在什麼情況下會改變他們是傻子時候的結果呢?
我們發現,拋開時間,就看剩餘天數\(d\)的奇偶性。如果是操作一,那麼每次操作都會改變\(d\)的奇偶性。所以改變\(d\)奇偶性的操作二啥用沒有,不考慮。
那麼什麼情況下的操作二不會改變\(d\)的奇偶性呢?跳偶數天。
我們發現,跳到下一月,實際跳了當前月的天數。(在跳月可行的情況下)
就拿當前日期為例。我們要從\(8.2\)跳到\(9.2\),實際是跳了\(8\)月天數\(31\)天而不是\(9\)月天數\(30\)天。
所以跳月能對答案造成影響的月份有:\(4,6,9,11\)
於是模擬,從給定日期往後跳,如果能跳的,就把計數器加一。
然後就是來討論這兩個數值如何推出最終答案。
記剩餘天數為\(k\),跳月能造成影響的次數為\(p\)。
根據\(k\),我們有一個初始的狀態表示是否必勝。(其實就是\(k\)為奇數必勝,\(k\)為偶數必敗)
然後來看\(p\)的影響。
a.如果我方本來就是必勝:
1.\(p\)是奇數。必敗。
我方必勝,對方必敗,有改變戰局的機會,對方肯定要使用。使用之後變為我方必敗,對方必勝的情況。這個時候還有偶數次改變戰局的機會。我方用一次,對方就對應的用一次。最終會導致我方保持必敗,對方保持必勝。
2.\(p\)是偶數。必勝。
我方必勝,對方必敗,有改變戰局的機會,對方肯定要使用。而此時對方每使用一次,我就對應的使用一次。最後會導致我方保持必勝,對方保持必敗。
b.如果我方本來就是必敗:
1.\(p\)是奇數。必勝。
同a1的情況,只是我們到了對方的位置。
2.\(p\)是偶數。必敗。
同a2的情況,只是我們到了對方的位置。
於是我們發現答案只和\(k,p\)的奇偶性有關。
但是這裡還有一個小小的特殊情況:
當\(p==0\)時,直接判斷\(k\)的奇偶性。\(k \mod 2=1\)必勝,否則必敗。
否則,判斷\((k+p)\)的奇偶性。\(((k+p) \mod 2=0)\)必勝,否則必敗。
這道題就水過去了。
程式碼中注意模擬的時候讓月份始終保持在\([1,12]\)中,不然像我這樣少一個細節就\(20pts\)拜拜了。
\(CODE\)
#include<bits/stdc++.h>
using namespace std;
int mon[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
int chg[13]={0,1 ,0 ,1 ,0 ,1 ,0 ,1 ,1 ,0 ,1 ,0 ,1 };
int rn(int y)
{
if(y%100==0&&y!=2000)return 0;
if(y%4==0)return 1;
return 0;
}
int last_days(int y,int m,int d)
{
int sum=0;
mon[2]=28+rn(y);
if(d>22)sum+=(mon[m]-d+22),m++;
else sum+=(22-d);
//先把日固定在22,一定能跳月
if(m==13)m=1,y++;
d=22;
while(y!=2012||m!=12||d!=22)
{
mon[2]=28+rn(y);
sum+=mon[m];
m++;
if(m==13)m=1,y++;
}
return sum;
}
int change_times(int y,int m,int d)
{
int t=0;
while(y!=2012||m!=12)
{
chg[2]=rn(y);
mon[2]=28+rn(y);
int nxt=m+1;
if(nxt>12)nxt=1;
if(d<=mon[nxt])
{
if(!chg[m])t++;
//如果此步不對答案產生貢獻,那就是一次轉機
}
else d=1;
m++;
if(m>12)m=1,y++;
}
return t;
}
int main()
{
int y,m,d;
while(~scanf("%d%d%d",&y,&m,&d))
{
int k=last_days(y,m,d);
//如果不跳月是否必勝
int p=change_times(y,m,d);
//是否能靠此扭轉戰局
if(!p)puts(k%2?"YES":"NO");
else puts((k+p)%2==0?"YES":"NO");
}
return 0;
}
一道類似的題目:洛谷\(P1512\)。
感謝閱讀。