粑粑博弈

粑粑博弈

  • 题目大意: 给定一个长度为 n 的非负整数数组 a 和一个正整数 k。Alice 和 Bob 轮流进行游戏,Alice 先手。
    每轮操作,当前玩家需要选择:
    一个正整数 x(1 ≤ x ≤ k)
    一个下标 y(1 ≤ y ≤ n)
    并且必须满足 x ≤ a_y。
    然后执行更新:
    k 变为 x
    a_y 变为 a_y - x

注意:如果玩家选择 x = 0 或无法做出有效操作,则立即判负。
问:在双方都采取最优策略的情况下,先手 Alice 是否有必胜策略?

  • 数据范围
    测试用例组数:1 ≤ t ≤ 10^5
    单组数组长度:1 ≤ n ≤ 10^6
    所有测试用例的 n 之和:∑ n ≤ 2 × 10^6
    初始上限:1 ≤ k ≤ 10^9
    数组元素:0 ≤ a_i ≤ 10^9

虽然游戏中的上限 k 会不断变化,但通过定义一个二进制进位边界 p(严格大于当前 k 的最小 2 的幂),可以将整个游戏压缩成经典的 Nim 游戏。胜负完全由所有 a_i 对 p 取模后的异或和决定。

对于任意正整数 k,定义函数:

\[P(k) = 2^{\lfloor \log_2 k \rfloor + 1} \]

即 严格大于 k 的最小的 2 的幂。
例如:P(5)=8, P(6)=8, P(8)=16。

该函数具有两个关键性质(在证明中会反复使用):

  1. 对于任意合法操作 x(满足 1 ≤ x ≤ k),必有 x < P(k)。
  2. 若 p = P(k),则 p/2 ≤ k < p。
  3. 任何新的上限 x 所对应的新模数 p' = P(x) 一定是旧模数 p 的约数(因为 p 和 p' 都是 2 的幂,且 p' ≤ p)。
  • 单堆情形的严格证明

状态:(k, a),当前上限为 k,堆大小为 a。

结论:单堆状态下,必败态 当且仅当:

\[a \bmod P(k) = 0 \]

  1. 证明“余数为 0 是必败态”(无法走向余数为 0)

设当前模数 p = P(k),且 a % p = 0。
当前玩家取 x(1 ≤ x ≤ k),新状态为 (x, a-x),新模数为 p' = P(x)。

  • 因为 x < p 且 p 是 2 的幂,所以 p' 整除 p。
  • 由于 a 是 p 的倍数,而 p' | p,因此 a 也是 p' 的倍数,即 a % p' = 0。
  • 计算新堆的余数:

    \[(a - x) \bmod p' = (0 - x) \bmod p' = (-x) \bmod p' \]

  • 由于 1 ≤ x < p'(因为 p' 是大于 x 的最小 2 的幂),所以 x 不可能是 p' 的倍数。
  • 因此 (-x) mod p' ≠ 0,即新状态的余数不为 0。

结论:从余数为 0 的状态出发,无论如何走,都会走到余数非 0 的状态(即必胜态)。符合“必败态”定义。

  1. 证明“余数非 0 是必胜态”(总能走向余数为 0)

设当前模数 p = P(k),余数 r = a % p,显然 1 ≤ r < p。由于 p/2 ≤ k < p,分两种情况构造取法 x:

情况 A:若 r ≤ k
直接取 x = r。

  • 取走后,a - x 是 p 的倍数。
  • 新模数 p' = P(x) 整除 p,所以 a - x 也是 p' 的倍数。
  • 新状态余数为 0,成功。

情况 B:若 r > k
令 q = p / 2,因为 r > k ≥ q,且 r < p = 2q,我们构造取 x = r - q。

  • 合法性验证:因为 r > q,所以 x ≥ 1;因为 r < 2q,所以 x < q ≤ k,故 1 ≤ x ≤ k,合法。
  • 计算新堆大小:

    \[a - x = a - (r - q) = (p \cdot t + r) - r + q = p \cdot t + q \]

    因为 p = 2q,所以:

    \[a - x = 2q \cdot t + q = q \cdot (2t + 1) \]

    即 a - x 是 q 的倍数。
  • 新模数 p' = P(x),因为 0 < x < q,所以 p' 整除 q。
  • 因此 a - x 也是 p' 的倍数,新状态余数为 0,成功。

结论:任意余数非 0 的状态,都能一步走到余数为 0 的状态。符合“必胜态”定义。

三、多堆情形的严格证明

状态:(k, a_1, ..., a_n),当前上限为 k。设 p = P(k)。

定义当前局面的 特征异或值 X:

\[X = (a_1 \bmod p) \oplus (a_2 \bmod p) \oplus \dots \oplus (a_n \bmod p) \]

其中 ⊕ 是按位异或运算。

结论:多堆状态下,必败态 当且仅当:

\[X = 0 \]

  1. 证明“X=0 是必败态”(无法走向 X=0)

设当前模数 p = P(k),且 X = 0。
当前玩家选择 x(1 ≤ x ≤ k)修改第 y 堆:a_y → a_y - x。新状态模数为 p' = P(x),显然 p' | p。

对于除了第 y 堆以外的所有堆,其模 p' 下的余数不变。
因为原 X=0,在模 p 下异或为 0。由于 p' 是 p 的因子(相当于只取二进制低位),我们可以推导出新异或值:

\[X' = \big( (a_y \bmod p) \bmod p' \big) \oplus \big( (a_y - x) \bmod p' \big) \]

如果 X' = 0,则必须有:

\[(a_y \bmod p') = (a_y - x) \bmod p' \]

这等价于要求 x ≡ 0 (mod p')。
但是,因为 1 ≤ x < p'(p' 是严格大于 x 的 2 的幂),x 不可能整除 p',矛盾。

结论:从 X=0 出发,所有走法都会导致 X' ≠ 0。符合“必败态”定义。

  1. 证明“X≠0 是必胜态”(总能走向 X=0)

设当前模数 p = P(k),且 X ≠ 0。利用经典 Nim 博弈的思路构造操作。

在模 p 下,找到异或和最高位为 1 的那一堆(设为第 t 堆)。设该堆当前余数为 b = a_t mod p。令 b' = b ⊕ X,根据异或性质必有 b' < b。令标准 Nim 需要取走的数量为 r = b - b'。

由于 p/2 ≤ k < p,分两种情况构造:

情况 A:若 r ≤ k
直接取 x = r。

  • 在模 p 下,第 t 堆余数变为 b',所有堆的异或和变为 0。
  • 新模数 p' = P(x) 整除 p。既然在模 p 下异或为 0,那么在模 p'(去掉高位)下异或也一定为 0。
  • 新状态 X' = 0,成功。

情况 B:若 r > k
令 q = p / 2,构造取 x = r - q。

  • 合法性:因为 r > k ≥ q,且 r < p = 2q,所以 1 ≤ r - q < q ≤ k,即 x 合法。
  • 取走后,第 t 堆在模 p 下的新余数为:

    \[b - x = b - (r - q) = b - r + q = b' + q \]

  • 新模数 p' = P(x),因为 0 < x < q,所以 p' 整除 q。因此 (b' + q) mod p' = b' mod p'。
  • 因为原构造保证了在模 p 下所有堆异或为 0,取模 p' 后(去除高位)依然为 0,所以新状态 X' = 0,成功。

结论:任意 X≠0 的状态,都能一步走到 X=0 的状态。符合“必胜态”定义。

#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false); cin.tie(nullptr);

    int t; cin >> t;
    while(t --) {
        int n;
        long long k; cin >> n >> k;

        long long p = 1;
        while(p <= k) p <<= 1;

        int xr = 0;
        for(int i = 0; i < n; i ++) {
            long long a; cin >> a;
            xr ^= (int)(a % p);
        }
        cout << (xr == 0 ? "Bob" : "Alice") << '\n';
    }

    return 0;
}

posted @ 2026-09-08 19:59  thenrify  阅读(16)  评论(0)    收藏  举报