https://www.luogu.org/problemnew/show/P2575
AKN玩游戏玩累了,于是他开始和同伴下棋了,玩的是跳棋!对手是wwx!这两位上古神遇在一起下棋,使得棋局变得玄幻莫测,高手过招,必有一赢,他们都将用最佳策略下棋,现在给你一个n*20的棋盘,以及棋盘上有若干个棋子,问谁赢?akn先手!
游戏规则是这样的:
对于一个棋子,能将它向右移动一格,如果右边有棋子,则向右跳到第一个空格,如果右边没有空格,则不能移动这个棋子,如果所有棋子都不能移动,那么将输掉这场比赛。
考虑每行为一个单独的sg博弈,n行sg值异或即可。将每行的状态以二进制表示,1表示存在棋子,0表示不存在。对每个状态去找他可以到的子状态,求sg值即可。注意可以预先预处理优化
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1000 + 5;
int sg[1 << 20];
bool vis[25]; //每个状态最多由19个状态转移而来
int Sg(int state) {
memset(vis, 0, sizeof(vis));
int tmp = 1, nxt = 0;
for(register int i = 1; i <= 20; i++) {
if(state & tmp) { //如果该位置有棋子
if((state | (tmp >> 1)) != state) vis[sg[state ^ tmp ^ (tmp >> 1)]] = 1;
//该位置的右边无棋子
else if(nxt) vis[sg[state ^ tmp ^ (1 << (nxt - 1))]] = 1;
}
else nxt = i; //保存跳过棋子可以到达的位置
tmp <<= 1; //枚举每个位置
}
tmp = 0;
while(vis[tmp]) ++tmp;
sg[state] = tmp;
}
int main()
{
for(register int i = 0; i < (1 << 20); i++) Sg(i); //预处理每个可能状态
int t, n, m, pos, ans;
scanf("%d", &t);
while(t--) {
scanf("%d", &n);
ans = 0;
for(register int i = 0; i < n; i++) {
scanf("%d", &m);
int state = 0;
for(register int j = 0; j < m; j++) {
scanf("%d", &pos);
state += 1 << (20 - pos);
}
ans ^= sg[state];
}
printf("%s\n", ans == 0 ? "NO" : "YES");
}
}