2025.02.25 CW 模拟赛 D. 博弈论
D. 博弈论
给我绕昏了.
思路
考虑什么时候会是 Draw. 可以发现若 Alice 和 Bob 手中的数相等, 那么异或和就为 0, 也就是整个序列的异或和为 0. 接下来考虑异或和不为 0 的情况.
不妨设序列异或和为 \(s\). 我们对于每一位考虑, 如果这一位上是 0, 那么在这一位上, Alice 和 Bob 即为平局 \((\)因为异或和为 0\()\); 如果这位是 1, 那么就可以决出胜负. 所以我们要考虑的位便成了 \(s\) 的最高位.
我们分 \(n\) 为奇数和偶数分别讨论:
-
\(n\) 为偶数, 先手必胜.
因为我们有奇数个 1, 那么在偶数位 / 奇数位上一定存在奇数个 1. 同时先手又可以任意控制后手选的位的奇偶性, 所以其一定可以选到奇数个 1, 先手必胜. \((\)举个例子, 序列为
10101011, 那么在奇数位上我们有奇数个 1. 如果先手选择第 1 个位置, 那么后手就只能选择第 2 / n 个位置, 这样一来, 相当于后手一直在选偶数位, 先手一直在选奇数位, 其就必胜\()\) -
\(n\) 为奇数.
如果序列两端都是 0, 无论先手选哪边都会转化为「\(n\) 为偶数, 有奇数个 1」的情况, 先手必败.
否则先手必然会取两端中的一个 1, 然后无论后手取哪个, 先手就必须跟着取, 不然就会转化为必败态.
所以我们可以先将相同的前后缀的消掉, 然后剩下的位置必须是「相邻的奇偶数位相同」先手才有可能胜 \((\)例如
001100...\()\), 否则后手必胜.另外, 若 1 的个数不为 4 的倍数, 先手和后手会将数字全部取完, 由于先手每次会与后手选择相同的数, 于是先手会选奇数个 1, 再加上最开始的一个 1, 总共偶数个 1, 输了. 所以先手胜的条件还有「1 的个数是 4 的倍数」.
总时间复杂度 \(\mathcal{O}(\sum n)\).
代码
#include "iostream"
using namespace std;
#define getbit(x) (31 - __builtin_clz(x))
constexpr int N = 1e5 + 10;
int n, a[N], s, cnt;
bool check(int l, int r) {
for (; a[l] == a[r]; ++l, --r);
for (int i = l; i <= r; i += 2)
if (a[i] ^ a[i + 1]) return false;
return true;
}
void init() {
cin >> n, s = cnt = 0;
for (int i = 1; i <= n; ++i) cin >> a[i], s ^= a[i];
int w = getbit(s);
for (int i = 1; i <= n; ++i) a[i] = a[i] >> w & 1, cnt += a[i];
}
void calculate() {
if (!s) return puts("Draw"), void();
if (!(n & 1)) return puts("Alice"), void();
if (!(a[1] + a[n]) or cnt % 4 ^ 1) return puts("Bob"), void();
int f = 0;
if (a[1]) f or_eq check(2, n);
if (a[n]) f or_eq check(1, n - 1);
puts(f ? "Alice" : "Bob");
}
void solve() {
cin.tie(nullptr);
int T; cin >> T;
while (T--) {
init();
calculate();
}
}
int main() {
solve();
return 0;
}

浙公网安备 33010602011771号