粑粑博弈
粑粑博弈
- 题目大意: 给定一个长度为
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,定义函数:
即 严格大于 k 的最小的 2 的幂。
例如:P(5)=8, P(6)=8, P(8)=16。
该函数具有两个关键性质(在证明中会反复使用):
- 对于任意合法操作
x(满足1 ≤ x ≤ k),必有x < P(k)。 - 若
p = P(k),则p/2 ≤ k < p。 - 任何新的上限
x所对应的新模数p' = P(x)一定是旧模数p的约数(因为p和p'都是 2 的幂,且p' ≤ p)。
- 单堆情形的严格证明
状态:(k, a),当前上限为 k,堆大小为 a。
结论:单堆状态下,必败态 当且仅当:
- 证明“余数为 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 的状态(即必胜态)。符合“必败态”定义。
- 证明“余数非 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=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' = 0,则必须有:
这等价于要求 x ≡ 0 (mod p')。
但是,因为 1 ≤ x < p'(p' 是严格大于 x 的 2 的幂),x 不可能整除 p',矛盾。
结论:从 X=0 出发,所有走法都会导致 X' ≠ 0。符合“必败态”定义。
- 证明“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;
}

浙公网安备 33010602011771号