1 条题解

  • 0
    @ 2026-5-19 20:43:58

    B. Lady Bug 详细题解

    一、问题重述

    给定两个长度为 nn 的 01 字符串 aabb。允许操作:对于 2in2 \le i \le n,可以交换 aia_ibi1b_{i-1},或交换 bib_iai1a_{i-1}。问能否通过任意次操作使得 aa 变为全 00


    二、核心观察

    2.1 操作的“交错”结构

    将两个字符串按如下方式交错排列,形成两条“折线”:

    • 折线 1a0,b1,a2,b3,a4,b5,a_0, b_1, a_2, b_3, a_4, b_5, \dots(偶数下标取自 aa,奇数下标取自 bb
    • 折线 2b0,a1,b2,a3,b4,a5,b_0, a_1, b_2, a_3, b_4, a_5, \dots(偶数下标取自 bb,奇数下标取自 aa

    操作的本质是在同一条折线上交换相邻两个位置的值(因为 aia_ibi1b_{i-1} 属于同一条折线,bib_iai1a_{i-1} 也属于同一条折线)。

    2.2 折线内的可达性

    在同一条折线上,相邻交换操作可以任意重排该折线上的字符。因此,每条折线上 01 的数量是守恒量。只要目标状态中每条折线上的 0 的数量不低于所需数量,就可以通过重排实现。

    2.3 目标状态分析

    目标:aa 全为 0,即 a0=a1==an1=0a_0=a_1=\dots=a_{n-1}=0

    • 折线 1a0,b1,a2,b3,a_0, b_1, a_2, b_3, \dots)需要的 0 数量:

      • a0,a2,a4,a_0, a_2, a_4, \dotsn/2\lceil n/2 \rceil 个位置必须为 0
      • 这些位置来自折线 1 中的 aa 部分,但 bb 部分无限制(因为目标只要求 aa 全 0)
    • 折线 2b0,a1,b2,a3,b_0, a_1, b_2, a_3, \dots)需要的 0 数量:

      • a1,a3,a5,a_1, a_3, a_5, \dotsn/2\lfloor n/2 \rfloor 个位置必须为 0

    2.4 必要条件

    设:

    • cnt1cnt_1 = 折线 1 中初始 0 的总数
    • cnt2cnt_2 = 折线 2 中初始 0 的总数

    则需满足:

    $$cnt_1 \ge \lceil n/2 \rceil \quad \text{且} \quad cnt_2 \ge \lfloor n/2 \rfloor $$

    三、充分性

    若上述条件满足,则:

    • 折线 1 中有足够的 0 填入 a0,a2,a4,a_0, a_2, a_4, \dots
    • 折线 2 中有足够的 0 填入 a1,a3,a5,a_1, a_3, a_5, \dots
    • 由于同一条折线内可以任意重排,总能实现 aa0

    因此条件是充要的。


    四、标程代码

    #include <iostream>
    using namespace std;
    
    void solve() {
        int n;
        cin >> n;
        string a, b;
        cin >> a >> b;
        
        int cnt1 = 0, cnt2 = 0;
        for (int i = 0; i < n; i++) {
            if (i % 2 == 1) {          // 奇数下标
                cnt2 += (a[i] == '0'); // 折线2 中的 a 部分(实际是折线1?需核对)
                cnt1 += (b[i] == '0');
            } else {                    // 偶数下标
                cnt1 += (a[i] == '0');
                cnt2 += (b[i] == '0');
            }
        }
        
        int need1 = (n + 1) / 2;   // ceil(n/2)
        int need2 = n / 2;          // floor(n/2)
        
        if (cnt1 >= need1 && cnt2 >= need2) {
            cout << "YES\n";
        } else {
            cout << "NO\n";
        }
    }
    
    int main() {
        ios_base::sync_with_stdio(false);
        cin.tie(nullptr);
        int t;
        cin >> t;
        while (t--) {
            solve();
        }
        return 0;
    }
    

    五、示例验证

    示例 1:n=3n=3, a=000a=000, b=000b=000

    • i=0i=0(偶):cnt1+=1cnt1+=1a[0]=0a[0]=0),cnt2+=1cnt2+=1b[0]=0b[0]=0)→ cnt1=1,cnt2=1cnt1=1,cnt2=1
    • i=1i=1(奇):cnt2+=1cnt2+=1a[1]=0a[1]=0),cnt1+=1cnt1+=1b[1]=0b[1]=0)→ cnt1=2,cnt2=2cnt1=2,cnt2=2
    • i=2i=2(偶):cnt1+=1cnt1+=1a[2]=0a[2]=0),cnt2+=1cnt2+=1b[2]=0b[2]=0)→ cnt1=3,cnt2=3cnt1=3,cnt2=3
    • need1=2,need2=1need1=2, need2=1,满足 → YES

    示例 2:n=6n=6,给定数据应得 YES

    示例 3:n=5n=5, a=10000a=10000, b=01010b=01010

    • 计算后 cnt1=3,cnt2=3cnt1=3, cnt2=3need1=3,need2=2need1=3, need2=2,满足 → 但答案是 NO?检查原题输出为 NO,说明我的条件可能反了。

    重新核对:折线 1 需要 n/2\lceil n/2 \rceil0 对应 aa 的偶数位(a0,a2,a4a_0,a_2,a_4)共 3 个,但折线 1 中的 0 可能分布在 aa 偶数和 bb 奇数。这里 cnt1cnt1 是折线 1 的总 0,如果不足 3 则不可行。原例中 a=10000a=10000, b=01010b=01010

    • i=0i=0a[0]=1a[0]=1cnt1cnt1 不加),b[0]=0b[0]=0cnt2cnt2 加1)
    • i=1i=1a[1]=0a[1]=0cnt2cnt2 加1),b[1]=1b[1]=1cnt1cnt1 不加)
    • i=2i=2a[2]=0a[2]=0cnt1cnt1 加1),b[2]=0b[2]=0cnt2cnt2 加1)
    • i=3i=3a[3]=0a[3]=0cnt2cnt2 加1),b[3]=1b[3]=1cnt1cnt1 不加)
    • i=4i=4a[4]=0a[4]=0cnt1cnt1 加1),b[4]=0b[4]=0cnt2cnt2 加1) 得 cnt1=2cnt1=2(来自 i=2,4i=2,4),cnt2=4cnt2=4need1=3need1=3cnt1=2<3cnt1=2<3NO

    六、总结

    步骤 操作
    1 将序列分为两条交错折线:(a0,b1,a2,b3,)(a_0,b_1,a_2,b_3,\dots)(b0,a1,b2,a3,)(b_0,a_1,b_2,a_3,\dots)
    2 每条折线内的字符可以任意重排(通过相邻交换)
    3 目标 aa00 要求第一条折线有 n/2\lceil n/2 \rceil00,第二条有 n/2\lfloor n/2 \rfloor00
    4 统计初始两条折线的 00 数量,判断是否满足条件

    核心:将操作抽象为两条折线上的相邻交换,从而转化为计数问题。

    • 1

    信息

    ID
    7277
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者