|
|
|
@ -1,59 +1,38 @@
|
|
|
|
|
#include <bits/stdc++.h>
|
|
|
|
|
#include <iostream>
|
|
|
|
|
#include <cstring>
|
|
|
|
|
using namespace std;
|
|
|
|
|
const int N = 55, M = 50050; // M 包括了 50 * 1000 + 50个石子数量为1的堆数
|
|
|
|
|
int f[N][M];
|
|
|
|
|
|
|
|
|
|
int dfs(int a, int b) {
|
|
|
|
|
int &v = f[a][b];
|
|
|
|
|
if (~v) return v;
|
|
|
|
|
// 简单情况: 即所有堆的石子个数都是严格大于1,此时a是0
|
|
|
|
|
if (!a) return v = b % 2; // 奇数为先手必胜,偶数为先手必败
|
|
|
|
|
|
|
|
|
|
// 一般5个情况 + 1个特殊情况
|
|
|
|
|
|
|
|
|
|
// 特殊情况: 如果操作后出现b中只有一堆,且堆中石子个数为1
|
|
|
|
|
// 那么应该归入到a中,并且b为0
|
|
|
|
|
// 以下所有情况,如果能进入必败态,先手则必胜!
|
|
|
|
|
if (b == 1) return dfs(a + 1, 0);
|
|
|
|
|
|
|
|
|
|
// 情况1:有a,从a中取一个
|
|
|
|
|
if (a && !dfs(a - 1, b)) return v = 1;
|
|
|
|
|
|
|
|
|
|
// 情况2, 4:有b,从b中取1个(石子总数 - 1) or 合并b中两堆(堆数 - 1),
|
|
|
|
|
if (b && !dfs(a, b - 1)) return v = 1;
|
|
|
|
|
|
|
|
|
|
// 情况3:有a >= 2, 合并a中两个
|
|
|
|
|
// 如果b的堆数不为0, a - 2, b + 1堆 + 2个石子(只需要加delta) ====> b + 3
|
|
|
|
|
// 如果b的堆数为0, a - 2, 0 + 2个石子 + 1堆 - 1 ====> b + 2
|
|
|
|
|
if (a >= 2 && !dfs(a - 2, b + (b ? 3 : 2))) return v = 1;
|
|
|
|
|
|
|
|
|
|
// 情况5:有a,有b, 合并a中1个b中1个, a - 1, b的堆数无变化 + 1个石子(只加delta)
|
|
|
|
|
if (a && b && !dfs(a - 1, b + 1)) return v = 1;
|
|
|
|
|
|
|
|
|
|
// 其他情况,则先手处于必败状态
|
|
|
|
|
return v = 0;
|
|
|
|
|
const int N = 55, M = 50060;
|
|
|
|
|
int n, f[N][M];
|
|
|
|
|
int sg(int a, int b) {
|
|
|
|
|
if (f[a][b] != -1) return f[a][b];
|
|
|
|
|
int &s = f[a][b];
|
|
|
|
|
if (!a) return b & 1; // 如果能转移到必败态
|
|
|
|
|
if (b == 1) return sg(a + 1, b - 1);
|
|
|
|
|
if (a && !sg(a - 1, b)) return s = 1; // 取a
|
|
|
|
|
if (b && !sg(a, b - 1)) return s = 1; // 合并b,取b
|
|
|
|
|
if (a && b > 1 && !sg(a - 1, b + 1)) return s = 1; // 合并a,b
|
|
|
|
|
if (a > 1 && !sg(a - 2, b == 0 ? b + 2 : b + 3)) return s = 1; // 合并a
|
|
|
|
|
return s = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
|
int T;
|
|
|
|
|
scanf("%d", &T);
|
|
|
|
|
memset(f, -1, sizeof f);
|
|
|
|
|
int T, n;
|
|
|
|
|
cin >> T;
|
|
|
|
|
f[1][0] = f[2][0] = 1;
|
|
|
|
|
f[3][0] = 0;
|
|
|
|
|
while (T--) {
|
|
|
|
|
cin >> n;
|
|
|
|
|
int a = 0, b = 0;
|
|
|
|
|
for (int i = 0; i < n; i++) { // n堆石子
|
|
|
|
|
scanf("%d", &n);
|
|
|
|
|
int a = 0, b = -1;
|
|
|
|
|
for (int i = 1; i <= n; i++) {
|
|
|
|
|
int x;
|
|
|
|
|
cin >> x;
|
|
|
|
|
if (x == 1) a++; // 每堆石子的数量
|
|
|
|
|
// b==0时 加1堆+加x石子=0 + 1+x-1=x
|
|
|
|
|
// b!=0时 加1堆+加x石子=原来的+x+1
|
|
|
|
|
// 偏移量是1个,在b=0时,需要考虑引入这个偏移量:-1,在b>0时,就不必再次考虑了
|
|
|
|
|
scanf("%d", &x);
|
|
|
|
|
if (x == 1)
|
|
|
|
|
a++;
|
|
|
|
|
else
|
|
|
|
|
b += b ? x + 1 : x;
|
|
|
|
|
b += x + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 1 为先手必胜, 0为先手必败
|
|
|
|
|
if (dfs(a, b))
|
|
|
|
|
if (b < 0) b = 0;
|
|
|
|
|
if (sg(a, b))
|
|
|
|
|
puts("YES");
|
|
|
|
|
else
|
|
|
|
|
puts("NO");
|
|
|
|
|