1 条题解

  • 0
    @ 2026-5-4 17:37:23

    CF1988B Make Majority 题解

    题意简述

    给定一个长度为 nn 的 01 序列 aa。每次操作可以选择一个区间 [l,r][l,r],统计区间内 00 的个数 c0c_011 的个数 c1c_1

    • c0c1c_0 \ge c_1,则将区间替换为一个 00
    • c0<c1c_0 < c_1,则将区间替换为一个 11

    问能否通过若干次操作使得最终序列变为 [1][1]


    核心分析

    目标是把整个序列合并成单个 11,这意味着我们需要尽量减少 00 的数量。

    关键观察 1:先合并连续的 00

    考虑任意一段连续的 00(形如 000...0,两端或一端有 11 隔开):

    • 如果我们单独对这一段连续的 00 做一次操作,显然 c0c1c_0 \ge c_1(因为 c1=0c_1=0),所以它一定会变成一个 00
    • 这相当于把「一段连续 00」压缩成一个 00,且该操作不会影响 11 的个数。

    因此,我们可以先执行这样的操作,把原序列中每一段连续的 00 都压缩成一个单独的 00。此时序列中不会出现相邻的两个 00

    关键观察 2:判断最终可能性

    压缩后,令整个序列中 00 的个数为 c0c_0(也就是原序列中连续 00 的段数),11 的个数为 c1c_1

    现在考虑能否通过后续操作使整个序列合并成 [1][1]

    • 如果 c0c1c_0 \ge c_1: 无论选择什么区间,区间内 00 的个数永远 1\ge 1 的个数(因为我们可以把 0011 配对,而 00 多了至少一个),所以任何合并操作都会倾向于产生 00。最终无论如何都无法消除所有的 00,故答案为 No

    • 如果 c0<c1c_0 < c_1: 我们直接选择整个序列 [1,n][1,n] 进行一次操作。此时区间内 c1>c0c_1 > c_0,合并结果恰好为 11,序列变为 [1][1]。故答案为 Yes

    因此,判定条件仅取决于:压缩后 00 的个数是否严格小于 11 的个数


    统计方法

    遍历一遍字符串:

    • 遇到 '1'c1 加一;
    • 遇到 '0',如果它是某个连续 00 块的开头(即 i=0i=0 或前一个字符是 '1'),则 c0 加一。

    最后比较 c0c_0c1c_1 即可。


    复杂度

    • 时间复杂度:O(n)O(n)
    • 空间复杂度:O(1)O(1)

    参考代码

    #include <bits/stdc++.h>
    using namespace std;
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
    
        int T;
        cin >> T;
        while (T--) {
            int n, c0 = 0, c1 = 0;
            string a;
            cin >> n >> a;
    
            for (int i = 0; i < n; i++) {
                if (a[i] == '1') {
                    c1++;
                } else {
                    if (i == 0 || a[i-1] == '1') {
                        c0++;
                    }
                }
            }
    
            if (c0 >= c1) {
                cout << "No\n";
            } else {
                cout << "Yes\n";
            }
        }
        return 0;
    }
    • 1

    信息

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