概念
一.必胜态与必败态
- 必胜态(N),最优策略下谁面临此状态谁必赢。
- 必败态(P),最优策略下谁面临此状态谁必输。
二.SG函数
- mex运算:定义为求最小的不属于此集合的非负整数。
- SG(x)中x表示状态。x是必败态当且仅当SG(x) = 0;
- 令 S 为所有x走一步可以到达的状态(必须合法)。那么有$SG(x) = mex(SG(y))(y ∈ S) $
- 可以暴力求骗分,主要是打表找规律
三.SG定理
- 游戏和的 SG 函数就是所有子游戏的 SG 函数的异或和,其中所有子游戏互相独立。
- 所以先手必胜当且仅当每堆石子的 SG 函数的异或和不为 0。
例题
取火柴游戏
分析
- 由SG定理判断初始状态,设SG = a1^a2^a3^...^an.如果SG == 0就lose
- 如果必胜,假设是a1中某些被取走,就令a1变成a'。有a'^a2^a3^...^an = 0 = SG ^ SG = SG^a1^a2^a3^...^an;
- 所以SG^a1 = a'.且必有a1 > a';
- 所以一旦有SG^ai < ai,就是在第i堆取走ai - SG^ai个。
#include<iostream> #include<cstdio> #include<cmath> #include<queue> #include<cstring> #include<algorithm> #define lson x<<1 #define rson x<<1|1 #define ll long long #define rint register int #define mid ((L + R) >> 1) using namespace std; template <typename xxx> inline void read(xxx &x) { char c = getchar(),f = 1;x = 0; for(;c ^ '-' && !isdigit(c);c = getchar()); if(c == '-') c = getchar(),f = -1; for(;isdigit(c);c = getchar()) x = (x<<1) + (x<<3) + (c ^ '0'); x *= f; } template<typename xxx>void print(xxx x) { if(x<0){putchar('-');x=-x;} if(x>9) print(x/10); putchar(x%10+'0'); } const int maxn = 500010; const int inf = 0x7fffffff; const int mod = 1e9 + 7; int SG; int n,a[maxn]; int main() { read(n); for(rint i = 1;i <= n; ++i) read(a[i]),SG ^= a[i]; if(!SG) {//当前即是必败态 printf("lose\n"); return 0; } for(rint i = 1;i <= n; ++i) { if((SG ^ a[i]) < a[i]) { printf("%d %d\n",a[i] - (SG ^ a[i]),i); for(rint j = 1;j <= n; ++j) { if(i ^ j) print(a[j]); else print(a[i] ^ SG); putchar(' '); } return 0; } } return 0; }