BZOJ1115 [POI2009]石子游戏Kam 【博弈论——阶梯游戏】
题目
有N堆石子,除了第一堆外,每堆石子个数都不少于前一堆的石子个数。两人轮流操作每次操作可以从一堆石子中移走任意多石子,但是要保证操作后仍然满足初始时的条件谁没有石子可移时输掉游戏。问先手是否必胜。
输入格式
第一行u表示数据组数。对于每组数据,第一行N表示石子堆数,第二行N个数ai表示第i堆石子的个数(a1<=a2<=……<=an)。 1<=u<=10 1<=n<=1000 0<=ai<=10000
输出格式
u行,若先手必胜输出TAK,否则输出NIE。
输入样例
2
2
2 2
3
1 2 4
输出样例
NIE
TAK
题解
首先我们了解一下阶梯游戏:
有一个n级的阶梯,每级阶梯上有若干硬币。二人轮流操作,每次可以将某一级台阶上的若干个硬币移到下一级。无法操作者输
这是一个SG游戏,我们尝试把它转化为Nim游戏来求解
经分析可以发现这个游戏只与奇数阶有关:
①假若对手移动偶数阶,我们可以继续将其往下移动到偶数阶,直至0阶
②假若对手移动奇数阶,若移动第一阶,则相当于取走
③假若对手移动奇数阶,且将移动到偶数阶,而偶数阶的子最终将以①的方式到达0阶而不改变先后手,所以也相当于取走
至此,可以完全转化为只与奇数阶有关的Nim游戏,求奇数阶异或和,非0则必胜
本题
本题和阶梯游戏有什么关系呢?
由于石子数单调不减,每堆石子之间有个差值
当我们移走第i堆的x个石子时,i与i - 1的差值减少x,i+1与i的差值增加x,不就是阶梯游戏么?
注意:这里N与N-1的差值是第一级台阶,相当于反过来的阶梯游戏
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long int
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)
using namespace std;
const int maxn = 1005,maxm = 100005,INF = 1000000000;
inline int RD(){
int out = 0,flag = 1; char c = getchar();
while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}
return out * flag;
}
int A[maxn],N;
int main(){
int T = RD();
while (T--){
N = RD(); int x = 0;
REP(i,N) A[i] = RD();
for (int i = N; i > 0; i--) A[i] -= A[i - 1];
for (int i = N; i > 0; i -= 2) x ^= A[i];
if (x) puts("TAK");
else puts("NIE");
}
return 0;
}