NIM游戏

发布时间 2023-07-07 17:29:44作者: ljfyyds

【模板】nim 游戏

题目描述

甲,乙两个人玩 nim 取石子游戏。

nim 游戏的规则是这样的:地上有 \(n\) 堆石子(每堆石子数量小于 \(10^4\)),每人每次可从任意一堆石子里取出任意多枚石子扔掉,可以取完,不能不取。每次只能从一堆里取。最后没石子可取的人就输了。假如甲是先手,且告诉你这 \(n\) 堆石子的数量,他想知道是否存在先手必胜的策略。

输入格式

本题有多组测试数据。

第一行一个整数 \(T\)\(T\le10\)),表示有 \(T\) 组数据

接下来每两行是一组数据,第一行一个整数 \(n\),表示有 \(n\) 堆石子,\(n\le10^4\)

第二行有 \(n\) 个数,表示每一堆石子的数量.

输出格式

\(T\) 行,每行表示如果对于这组数据存在先手必胜策略则输出 Yes,否则输出 No

样例 #1

样例输入 #1

2
2
1 1
2
1 0

样例输出 #1

No
Yes

结论

如果有 \(n\) 堆石子,每堆石子的个数分别为:\(a_1, a_2, a_3, a_4......a_n\),若: \(a_1 \oplus a_2 \oplus a_3 \oplus a_4 ... \oplus a_n \neq 0\) 则先手必胜,否则先手必输。

操作到最后的时候是 \(0\oplus0\oplus0\oplus...\oplus0=0\)

在操作过程中 如果 \(a_1 \oplus a_2 \oplus a_3 \oplus a_4 ... \oplus a_n \neq 0 = x\) 那么我们可以将结果异变成 \(0\)

证明

不妨设 \(x\) 的二进制最高位 \(1\) 在第 \(k\) 位,那么 \(a_1,a_2,a_3,...,a_n\) 中,必然有一个数字 \(a_i\)\(k\) 位为 \(1\)

因为 \(x\) 是异或而来的。\(a_i \oplus x<a_i\) (异或后,第 \(k\) 位会变成 \(0\))。

从第 \(i\) 堆石子拿走 \(a_i-(a_i \oplus x)\) 个石子,第 \(i\) 堆还剩

\[a_i-[a_{i}-(a_i \oplus x)] \]

\[=a_i-a_i-(a_i \oplus x) \]

\[=a_i \oplus x \]

此时 \(a_1 \oplus a_2 \oplus a_3 \oplus a_4 ... \oplus a_n \oplus x= x \oplus x=0\)

那么这时候通过此项操作,我们可以把操作分为两种情况

1. 如果先手面对的局面是 \(a_1 \oplus a_2 \oplus a_3 \oplus a_4 ... \oplus a_n \neq 0\)

那么先手总可以通过拿走某一堆若干个石子,将局面变成\(a_1 \oplus a_2 \oplus a_3 \oplus a_4 ... \oplus a_n = 0\) 后手只能 \(a_1 \oplus a_2 \oplus a_3 \oplus a_4 ... \oplus a_n \neq 0\) 的情况。

由于游戏一定可以结束,从而到最后的时候先手拿走最后一些石子,留给后手无石子可拿,先手必胜。

2. 如果先手面对的局面是 \(a_1 \oplus a_2 \oplus a_3 \oplus a_4 ... \oplus a_n = 0\)

那么无论先手怎么拿,都会将局面变成 \(a_1 \oplus a_2 \oplus a_3 \oplus a_4 ... \oplus a_n \neq 0\)那么后手总可以通过拿走某一堆若干个石子,将局面变成 \(a_1 \oplus a_2 \oplus a_3 \oplus a_4 ... \oplus a_n = 0\)

如此重复,最后一定是先手面临最终没有石子可拿的状态。先手必败。

#include <cstdio>
#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 == 0)
        puts("No");
    else
        puts("Yes");
    return 0;
}