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;
}
posted @ 2025-02-25 19:25  Steven1013  阅读(13)  评论(0)    收藏  举报