给定一个n*20的棋盘,在棋盘上有m个棋子,每个棋子的合法移动是当这个棋子右边没有棋子时,将这个棋子移动到右边一格,当右边有棋子时,到其右边下一个位置直到第一个空的位置。
因为棋子的移动只在行上,所以容易想到多堆的nim博弈。因为每行20个空格。所以刚好可以状压枚举所有情况的sg值。
刚开始不知道sg值的范围所以用的是set来保存,结果TLE,最后打表看了一下最大值只有20.。。
最后结果就是SG(1)^sg(2)..^sg(n)
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <map>
#include <stack>
#include <queue>
#include <string.h>
#include <string>
#include <iomanip>
#include <cstring>
#include <vector>
#include <functional>
#include <cmath>
#include <sstream>
#include <set>
using namespace std;
#define PI acos(-1.0)
#define sp system("pause")
typedef long long ll;
typedef unsigned long long ull;
const ll mod = 1000000007;
#define PI acos(-1.0)
int sg[(1 << 21) + 20];
bool vis[21];
void init()
{
memset(sg,0, sizeof sg);
int len = 1 << 20;
for (int i = 0; i < len;i++)
{
memset(vis, false, sizeof vis);
for (int j = 19; j >= 0;j--)
{
if (i&(1 << j))
{
int k = j-1;
while (k >= 0 && (i&(1 << k)) != 0)k--;
if (k >= 0)
{
int tmp = i;
tmp ^= (1 << j);
tmp ^= (1 << k);
vis[sg[tmp]] = true;
}
}
}
for (int j = 0; j < len;j++)
if (!vis[j])
{
sg[i] = j; break;
}
}
}
int main()
{
init();
int T;
cin >> T;
while (T--)
{
int n;
int ans = 0;
cin >> n;
for (int i = 0; i < n; i++)
{
int tmp=0;
int m;
scanf("%d", &m);
for (int j = 0; j < m; j++)
{
int x;
scanf("%d", &x);
x = 20 - x;
tmp ^= (1 << x);
}
ans ^= sg[tmp];
}
if (ans)puts("YES");
else puts("NO");
}
}