YC311A [ 20240701 CQYC省选模拟赛 T1 ] 好串(good)
题意
给定一个长度为 \(n\) 的 \(01\) 串。
定义一个串是好的当且仅当该串的所有前缀以及所有后缀的 \(1\) 的数量大于等于 \(0\) 的数量。
你需要维护 \(q\) 个查询,每次求 \(S_{l, ..., r}\) 的子串最少添加的 \(1\) 的个数使得该子串是好的。
Sol
首先不难发现一个正确的贪心,也就是对于前缀跑一遍,尽量把 \(1\) 的位置往后放,然后再倒着跑一遍,此时需要添加的位置显然是最优的。
想办法用一些数据结构去维护这个过程。
先形式化这个东西,设 \(0\) 为 \(-1\),\(1\) 为 \(+1\),我们设前缀和数组为 \(sum_i\) 对于第一遍操作后的数组 \(sum'\),当前前缀放置的 \(1\) 个数显然有 \(sum'_i = \max_{j = 1} ^ i -sum_i\)。
第一遍操作的贡献即为 \(max_{i = 1} ^ n (-sum_i)\)。
考虑第二遍操作所带来的贡献,还是考虑 \(sum'\) 数组的折线图,若有 \(sum'_i > sum_n\) 意味着什么?也就是对于区间 \(i + 1 \to n\) 的 \(0\) 的个数大于 \(1\) 的个数。
所以,第二遍操作的贡献为 \(\max_{i = 1} sum'_i - sum_n\)。
将 \(sum'_i\) 的式子带入,\(sum_n\) 不变可以提出来。
最后加上第一遍的贡献。
欸我们发现最后这两个东西加起来等于零,直接暴力消掉。
于是最后式子就变成了: \(\max \{sum_i - sum_j, j \le i\} - sum_n\)。
如何支持多组询问?中间 \(sum_i - sum_j\) 会直接减掉 \(sum_{l - 1}\),直接把 \(sum_n\) 改成 \(sum_r - sum_{l - 1}\) 即可。
最后这个东西可以用线段树维护,或者使用并查集离线维护。
复杂度:\(O(n \log n)\)。