假设存在至多 $m$ 个巧克力棒长度的异或和是 $0$ ,且剩下的那些异或和不是 $0$ ,那么只要先手第一次操作只要把这 $m$ 个拿出来,剩下的就是必败态了。
具体证明的话不难,先看 $m=n$ 的情况。那么全拿出来之后,剩下的吃巧克力环节就是纯粹的 nim 游戏了。此时后手相当于先手。那么后手面临必败态就说明先手面临必胜态。
如果 $m<n$ 的话,那么把这 $m$ 个当做一个子游戏,把柜子里的剩下 $n-m$ 个当做至少一个子游戏。后续只要先手不从柜子里拿,就等后手拿就行。因为剩下 $n-m$ 个一定凑不出异或为 $0$ 的解,所以只要后手把他们拿出来,这个子游戏就一定是先手赢。而先手第一步拿出来的这些上述已经说明了是先手胜。
所以我们的问题就变成了,能否选出极大的巧克力棒使得异或和是 $0$ ,剩下的异或和非零。一种最简单的方法就是状压枚举,复杂度 $O(n2^n)$ 。
但是我们还可以用异或高斯消元来完成。我们把巧克力棒长度的 30 位拆开,每一位当做一个方程,构成一个 30 个方程 $m$ 个变量的方程组。如果只有唯一解的话,一定是全为 0 ,此时先手必败。只要有多组解,那就一定是先手必胜。
#include <stdio.h>
#include <string.h>
#include <algorithm>
const int M = 32, N = 16;
int m, c[N];
bool a[M][N];
// x_{i,0} a_0 + ... x_{i,v_cnt-1} a_{v_cnt-1} = a_{v_cnt} 0\le i<eq_cnt
int gauss(int eq_cnt, int v_cnt)
{
int c = 0, r = 0;
for (; c < v_cnt; ++c)
{
int t = r;
for (int i = r; i < eq_cnt; ++i)
if (a[i][c])
t = i;
if (!a[t][c])
continue;
for (int i = c; i <= v_cnt; ++i)
std::swap(a[r][i], a[t][i]);
for (int i = r + 1; i < eq_cnt; ++i)
if (a[i][c])
for (int j = v_cnt; j >= c; --j)
a[i][j] ^= a[r][j];
++r;
}
if (r < v_cnt)
{
for (int i = r; i < eq_cnt; ++i)
if (a[i][v_cnt])
return -1;
return 1;
}
for (int i = v_cnt - 1; i >= 0; --i)
for (int j = i + 1; j < v_cnt; ++j)
a[i][v_cnt] ^= a[i][j] & a[j][v_cnt];
return 0;
}
void solve()
{
scanf("%d", &m);
for (int i = 0; i < m; ++i)
scanf("%d", &c[i]);
for (int i = 0; i < 30; a[i][m] = 0, ++i)
for (int j = 0; j < m; ++j)
a[i][j] = (c[j] >> i) & 1;
puts(gauss(30, m) > 0 ? "NO" : "YES");
}
int main()
{
int T = 10;
while (T--)
solve();
}