A Bit Odd

A Bit Odd

问题描述

给定长度为 n 的二进制字符串 s(只含 0 和 1)。
Alice 和 Bob 轮流操作,Alice 先手。
每次操作必须选择一个 子序列,该子序列的 逆序对数量为奇数,然后删除它。
无法操作的玩家输。
判断在最优策略下谁获胜。


前置知识

  • 逆序对:在二进制串中,若 i < j 且 s[i] = '1'、s[j] = '0',则 (i, j) 为一个逆序对。
  • 子序列:从原串中删除若干(可能零个或全部)字符后得到的序列。

核心观察

1. 全 0 或全 1 的串是必败态

  • 没有任何逆序对,当前玩家无法操作,直接输。

2. 先手能否一步清空整个串?

情况 A:删除所有 1

  • 取所有 1 作为子序列(可以带任意 0)。
  • 逆序对总数 = 每个被选中的 0 前面被选中的 1 的个数之和。
  • 存在某个 0 前方有奇数个 1 ⇔ 可以构造奇数逆序对子序列,从而一次删完所有 1,直接获胜。

情况 B:删除所有 0

  • 对称地,存在某个 1 后方有奇数个 0 ⇔ 可以一次删完所有 0,直接获胜。

若上述任一条件成立,Alice 第一步即可获胜。


必败态的结构

若上述两个条件均不成立,则字符串必为以下形式:
0 (XX) (XX) ... (XX) 1

其中每一对 (XX) 要么是 "00",要么是 "11"。
开头的 0 和结尾的 1 是可选的(可能不存在)。

特点:

  • 每个 0 前的 1 的个数都是偶数;
  • 每个 1 后的 0 的个数都是偶数;
  • 总逆序对数为偶数,且任何合法操作都会把胜利交给对手,因此这种状态是 必败态。

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

void solve() {
    int n; cin >> n; string s; cin >> s;
    int k1 = 0, k0 = 0;
    bool flag = false;
    for(int i = 0; i < n; i ++) {
        if(s[i] == '1') k1 ++;
        else {
            if(k1 & 1) flag = true;
        }
    }
    for(int i = n - 1; i >= 0; i --) {
        if(s[i] == '0') k0 ++;
        else {
            if(k0 & 1) flag = true;
        }
    }
    cout << (flag ? "Alice" : "Bob") << "\n";
}

int main() {
    ios::sync_with_stdio(false); cin.tie(nullptr);
    int t = 1; cin >> t;
    while(t --) solve();
    return 0;
}
posted @ 2026-09-08 20:10  thenrify  阅读(9)  评论(0)    收藏  举报