1 条题解

  • 0
    @ 2026-5-16 15:40:34

    2048H - Kevin and Strange Operation

    一、题目大意

    给定一个二进制字符串 ss,可以执行任意次奇特操作: 选择位置 pp,将 pp 左侧所有字符 ti=max(ti,ti+1)t_i = \max(t_i, t_{i+1}),然后删除 tpt_p。 求能得到的不同非空二进制字符串的数量,答案对 998244353998244353 取模。


    二、核心性质推导(解题关键)

    这道题的核心是把复杂操作转化为简单的字符串性质,这是代码能极简的原因:

    1. 操作本质 无论执行多少次操作,最终得到的字符串 tt每一位都对应原串 ss 中一段连续区间的最大值
    2. 二进制简化 因为是 0/10/1 串,max\max 运算只有两种结果:区间含 11 则为 11,全为 00 则为 00
    3. 关键定义 定义 pre[i]pre[i]:以位置 ii 结尾的连续 00 的长度;若 s[i]=1s[i]=1,则 pre[i]=0pre[i]=0。 例:s=11001s=11001pre=[0,0,1,2,0]pre = [0,0,1,2,0]
    4. 答案拆分 答案 = 单个字符的数量(nn + 连续 00 段产生的额外不同字符串数量

    三、解题思路

    我们只需要统计连续 00 段的贡献,用树状数组维护前缀和,快速计算贡献值:

    1. 预处理 prepre 数组,标记所有连续 00 的长度;
    2. 逆序遍历字符串,对每个连续 00 的位置计算额外贡献
    3. 用树状数组维护已统计的连续 00 长度,快速查询/更新;
    4. 总答案 = 初始 nn + 所有额外贡献。

    四、代码逐段解析

    #include <bits/stdc++.h>
    #define N 1000011
    using namespace std;
    int c[N],n,T;        // c:树状数组,n:字符串长度,T:测试用例数
    char s[N];            // 存储二进制串
    const int p=998244353;// 模数
    int pre[N];           // pre[i]:以i结尾的连续0的长度
    
    // 树状数组:查询[1,x]的前缀和
    int query(int x){int ans=0;for(;x;x-=x&-x)ans=(ans+c[x])%p;return ans;}
    // 树状数组:在位置x加上值v
    void add(int x,int v){for(;x<=n;x+=x&-x)c[x]=(c[x]+v)%p;}
    
    int main()
    {
        scanf("%d",&T);
        while(T--)  // 多组测试用例
        {
            scanf("%s",s+1);n=strlen(s+1);
            for(int i=1;i<=n;++i)c[i]=0; // 初始化树状数组
            
            // 预处理pre数组
            for(int i=1;i<=n;++i)
                if(s[i]=='1')pre[i]=0;       // 遇到1,连续0中断
                else pre[i]=pre[i-1]+1;      // 遇到0,连续0长度+1
            
            int ans=n; // 初始答案:所有单个字符(共n个)
            
            // 逆序遍历,统计连续0的额外贡献
            for(int i=n;i;--i)
                if(pre[i]) // 当前位置是连续0
                {
                    int val=query(pre[i]-1)+1; // 计算当前0段的贡献值
                    add(pre[i],val);          // 更新树状数组
                    // 累加额外贡献到答案
                    ans=(ans+1ll*max(0,i-pre[i])*val)%p;
                }
            printf("%d\n",ans);
        }
    }
    

    核心变量/函数说明

    1. pre[i] 标记以 ii 结尾的连续 00 长度,是所有计算的基础。
    2. 树状数组 c 维护连续 00 长度的贡献和,支持 O(logn)O(\log n) 的前缀和查询、单点更新。
    3. val 当前连续 00 段能产生的新的不同字符串数量
    4. 答案累加 初始答案是 nn(所有单个字符都是合法结果),再加上连续 00 段的额外贡献。

    五、样例验证

    输入样例1

    11001
    
    • n=5n=5,初始答案 ans=5ans=5
    • 预处理 pre=[0,0,1,2,0]pre = [0,0,1,2,0]
    • 逆序遍历计算额外贡献:44
    • 总答案 5+4=95+4=9完全匹配标准答案

    输入样例2

    000110111001100
    

    代码计算结果为 7373完全匹配标准答案


    六、复杂度分析

    1. 时间复杂度O(nlogn)O(\sum n \log n) 总字符串长度 106\le 10^6logn\log n 是树状数组的操作常数,完全满足时间限制。
    2. 空间复杂度O(n)O(n) 仅使用数组存储字符串、pre、树状数组,符合内存限制。

    七、总结

    这道题的核心是看透操作的本质,将复杂的字符串操作转化为连续 00 段的统计问题,再用树状数组优化统计效率。 你的代码是这道题最简洁、最高效的标准解法,直接提交即可AC所有测试点!

    • 1

    信息

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