分析
-
最后无法进行操作相当于这n堆石子都被拿完了。
-
对于Nim游戏,存在如下定理:假设这n堆石子的个数分别为 $a_1, a_2, …, a_n$,当$a_1 \oplus a_2 \oplus … \oplus a_n = 0$ 时先手必败,否则先手必胜(其中$\oplus$指异或运算)。
-
下面证明这个结论。分为两种情况讨论:
-
情况一:当所有堆石子都是0个时,$0 \oplus 0 \oplus … \oplus 0 = 0$,此时的先手(此时需要操作的人)必败。
-
情况二:当存在某些堆石子个数不为0时,这又可以分为两种情况讨论:
(1)$a_1 \oplus a_2 \oplus … \oplus a_n = x$ 并且 $x \neq 0$,下面证明我们一定可以通过一步合法操作,使其操作后的异或值为0。
假设x
的二进制表示中最高一位1在第k位,则 $a_1$~$a_n$中一定存在奇数个数据且它们的第k为也为1(可以使用反正法证明),从中取出一个数据,假设为$a_i$,其第k位是1,则一定有$x \oplus a_i < a_i$,我们从第i
堆中拿走$a_i - x \oplus a_i$个石子,则还剩余$x \oplus a_i$个石子,这一步操作之后,则:
$$
a_1 \oplus a_2 \oplus … \oplus a_i \oplus x \oplus … \oplus a_n = x \oplus x = 0
$$
(2)如果 $a_1 \oplus a_2 \oplus … \oplus a_n = 0$,证明无论此时进行何种合法操作都一定会使得异或值不为0。可以使用反正法,假设第i
堆被拿走一些石子,还剩余 $a_i’$ 个石子,且此时使得 $a_1 \oplus a_2 \oplus … \oplus a_i’ … \oplus a_n = 0$,将该式和 $a_1 \oplus a_2 \oplus … \oplus a_n = 0$ 等号两边分别异或,可以得到 $a_i \oplus a_i ‘ = 0$,则可以推出$a_i = a_i’$,因为必须从某堆中拿走一些石子,不能不拿,矛盾。因此如果 $a_1 \oplus a_2 \oplus … \oplus a_n = 0$,无论怎么操作都会使其不等于0。
- 因此如果先手对应的局面异或值不等于0,则进行一步操作一定可以让其异或值为0,然后对方操作一步后一定会使得异或值不为0,如此反复,一定是对方先遇到全0的局面,因此先手必胜,对方必败。否则先手必败,对方必胜。
代码
#include <iostream>
using namespace std;
int main() {
int n;
scanf("%d", &n);
int res = 0;
for (int i = 0; i < n; i++) {
int x;
scanf("%d", &x);
res ^= x;
}
if (res) puts("Yes"); // 非零, 先手胜
else puts("No");
return 0;
}