Definition
這樣的游戲被稱為Nim游戲:
1、有兩個玩家,輪流進行操作
2、是公平游戲。即面對同一局面兩個玩家所能進行的操作是相同的。例如中國象棋不是公平游戲。因為面對同一個局面,紅方只能移動紅色棋子而不能移動黑方棋子,黑房同理。
3、一個玩家是輸掉當且僅當他無法進行操作。例如如果是兩個人輪流取石子的游戲,那么一個玩家輸掉當且僅當他面前沒有石子了。因為他下面無法進行取石子的操作。
一般的,Nim游戲指兩個玩家輪流在好幾堆東西中取物品,不能誇堆取,無法操作判負。
Solution
對於Nim游戲的解法,因為狀態顯然是不可逆的,所以可以對狀態的轉移圖進行拓撲排序然后DP求解。但是對於大部分博弈題目,狀態多的難以計算,所以需要考慮更簡單的方法。
結論:一個Nim游戲中的狀態是必敗狀態當且僅當每個子游戲的異或和為0.
這里子游戲代表構成Nim游戲中最基礎的游戲。例如兩個人輪流取三堆石子的游戲是三個取一堆石子的游戲組合而成的。對於每個子游戲顯然可以用一個正整數代表當前的狀態,即還剩多少石子。Nim游戲的子游戲狀態異或和為\(0\),則必敗。
證明:我有一個絕妙的想法,可惜這里寫不開
對於一個異或和不為\(0\)的狀態,那么他是必勝態。
考慮在一個必勝態下如何進行下一步。
設異或和是\(X\)。設\(X\)二進制的最高為1位是第\(k\)位。
那么顯然存在至少一個狀態使得他們二進制第\(k\)位為1。否則第k位異或和應該是0。
那么就任選一個第k位是1的狀態,設他的狀態是\(Y\),那么將他可以將\(Y\)這一堆取成\(Z=X~xor~Y\)那么得到的狀態是必敗狀態。
證明如下:
首先,\(Y\)可以取成\(Z\)當且僅當\(Z~\leq~Y\)。首先證明\(Z~\leq~Y\)。
因為\(Y\)和\(X\)第\(k\)位都是\(1\),更高位都是\(0\)。那么異或后\(Z\)第\(k\)位一定是\(0\),更高位顯然是\(0\)。所以\(Z\)的位數比\(Y\)的位數要少。那么顯然\(Z~\leq~Y\)。
下面證明更改后的狀態是必敗狀態
根據\(Z=X~xor~Y\),並且\(A~xor~A=~0~\)。可得:
更改后的狀態
\(S~=~X~xor~Y~xor~Z~=~X~xor~Y~xor~(~X~xor~Y~)\)
\(=~(~X~xor~Y~)~xor~(~X~xor~Y~)~=~0~\)
一定是一個必敗狀態。
Example
Description
輸入k及k個整數n1,n2,…,nk,表示有k堆火柴棒,第i堆火柴棒的根數為ni;接着便是你和計算機取火柴棒的對弈游戲。取的規則如下:每次可以從一堆中取走若干根火柴,也可以一堆全部取走,但不允許跨堆取,也不允許不取。
Input
第一行,一個正整數k
第二行,k個整數n1,n2,…,nk
Output
如果是先取必勝,請在第一行輸出兩個整數a,b,表示第一次從第b堆取出a個。第二行為第一次取火柴后的狀態。如果有多種答案,則輸出<b,a>字典序最小的答案(即b最小的前提下a最小)。
如果是先取必敗,則輸出“lose”。
Sample Input_1
3
3 6 9
Sample Output_1
4 3
3 6 5
Sample Input_2
4
15 22 19 10
Sample Output_2
lose
Hint
\(k~\leq~500000\)
\(n_i~\leq~1e9\)
Solution
板子題要啥solution
Code
#define rg register
#define ci const int
#define cl const long long int
typedef long long int ll;
namespace IO {
char buf[90];
}
template<typename T>
inline void qr(T &x) {
char ch=getchar(),lst=' ';
while(ch>'9'||ch<'0') lst=ch,ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
if(lst=='-') x=-x;
}
template<typename T>
inline void write(T x,const char aft,const bool pt) {
if(x<0) x=-x,putchar('-');
int top=0;
do {
IO::buf[++top]=x%10+'0';
x/=10;
} while(x);
while(top) putchar(IO::buf[top--]);
if(pt) putchar(aft);
}
template<typename T>
inline T mmax(const T a,const T b) {if(a>b) return a;return b;}
template<typename T>
inline T mmin(const T a,const T b) {if(a<b) return a;return b;}
template<typename T>
inline T mabs(const T a) {if(a<0) return -a;return a;}
template<typename T>
inline void mswap(T &a,T &b) {
T temp=a;a=b;b=temp;
}
const int maxn = 500010;
int n,ans;
int MU[maxn];
int main() {
qr(n);
for(rg int i=1;i<=n;++i) {
qr(MU[i]);ans^=MU[i];
}
if(!ans) {
puts("lose");return 0;
}
int _temp=1<<30;
while(!( _temp & ans )) _temp>>=1;
for(rg int i=1;i<=n;++i) if(MU[i] & _temp){
int z=ans^MU[i];
write(MU[i]-z,' ',true);write(i,'\n',true);
for(rg int j=1;j<=n;++j) {
if(j != i) write(MU[j],' ',true);
else write(z,' ',true);
}
break;
}
return 0;
}
Summary
Nim游戲是必敗態當且僅當子游戲狀態異或和為0。否則是必勝態。通過必勝態一定可以通過一步操作變成必敗狀態。