D. Agnej
博弈,m 为偶数的时候最后一定是变成每层两半各留一个,直接统计 1 的数目判断奇偶性;
m 为奇数的时候先默认大家都会薅掉中间的,这样还是每层两半各留一个。但是只要某层某一半没了,中间还在,中间就不能被薅掉了。
假设某一层中间被包裹得很好,例如 1 1 1 1 1,至少需要薅掉两个旁边的才能被保护,这样由于 Alice 和 Bob 里面一定有一个想在中间那个被保护之前薅掉它,它就一定会被薅掉,最后和 m 为偶数的情况一样,剩下类似于 0 1 0 0 1 的东西,剩下 2 个 1。
假设某一层中间没有被包裹,例如 0 0 1 1 1,这个 1 不能被薅掉,因此最后这层一定变成 0 0 1 0 0,只会剩下一个 1。
假设只有一阶包裹,例如 0 1 1 1 1,且只有一层符合这个要求,假设一旦这个 1 被保护 Alice 就寄了,Alice 会把它薅掉,Alice 就能赢。假设 1 被薅掉 Alice 才寄,Alice 可以把这个 1 保护起来(抽掉旁边的 1,变成 0 0 1 1 1),Alice 还是能赢。所以假设只有一层有一阶包裹,就能导致 Alice 必胜。
假设有偶数个层有一阶包裹,且如果大家都薅掉中间的,Alice 会寄,Alice 就只能选择保护,但这样 Bob 也可以跟着保护一个,Alice 扭转不了局面。这个情况换 Bob 来也一样。
假设有奇数个层有一阶包裹,偶数部分会被像上面那样被浪费掉,最后剩下一个,导致 Alice 必胜。
所以可以这样编码:
#include <algorithm>
#include <iostream>
#define int long long
const int N = 1e5 + 4;
int n, m;
char s[N];
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
std::cin >> n >> m;
// 虚空视为 bob 赢
int res = 0;
// 一次阻止中间薅掉能否逆转局势?(可以被 alice 破坏)
int blk1 = 0;
for (int i = 0; i < n; i++) {
std::cin >> s;
for (int j = 0; j < m; j++) {
if (s[j] == '1') {
// 多一个就逆转一次局势
res ^= 1;
}
}
// 处理中间有 1 的情况
if (m % 2 == 1 && s[m / 2] == '1') {
// 统计两侧 1 的数量
int lc = 0, rc = 0;
for (int j = 0; j < m / 2; j++) {
if (s[j] == '1') {
lc++;
}
}
for (int j = m / 2 + 1; j < m; j++) {
if (s[j] == '1') {
rc++;
}
}
// 最少多少次阻止中间被薅掉
int blk = std::min(lc, rc);
// 中间薅不掉,局势逆转
if (blk == 0) {
res ^= 1;
}
// 一次阻止中间薅掉,总共有奇数次这种情况才逆转局势,否则可以 replica
if (blk == 1) {
blk1 ^= 1;
}
}
}
std::cout << (res || blk1 ? "Alice" : "Bob") << '\n';
return 0;
}