这道题的问题非常自然,而解法也极其多样,感觉上和今年的 CSPT4 非常相似,是一道不可多得的好 DS!
几个做法的方向都是考虑区间查询与原串的区别,这也是这类问题一个常用的手法,但是考察的方向不一样就导出了不同的做法。
官解
不难发现,一次操作区间 $[l,r]$ 和全局的区别只有 $l$ 这个位置是否删除。
而维护全局迭代 $k$ 次后的结果是容易的,如果能够求出 $[l,r]$ 迭代 $k$ 次之后,左端点 $l$ 对应到原序列的位置 $L$,那么只需要统计全局迭代 $k$ 次之后 $[L,r]$ 里还剩下了多少字符即可。
于是我们考虑怎么维护每个询问 $[l,r]$ 迭代 $k$ 次之后,左端点对应到的原序列位置 $L_i$。
初始时 $L_i=l_i$,我们考虑一轮操作:
如果全局中 $L_i$ 被删了,区间中自然也被删了,于是 $L_i$ 要变成之后第一个存在的位置。
如果全局中 $L_i$ 没被删,但区间中也要删掉这个位置,于是 $L_i$ 要变成之后第一个存在的位置。
所以无论如何 $L_i$ 都会变成之后第一个存在的位置,于是把询问离线,每次迭代我们要做的事情就是:
把全局中应该删除的位置删除
把所有询问的 $L_i$ 变成之后存在的第一个位置
前者是容易维护的,但是后者看上去很难做。
此时一个很巧妙的想法是,我们维护所有剩余元素的相对位置,然后把 $L_i$ 的定义改成,每个询问 $[l,r]$ 迭代 $k$ 次之后,左端点对应到现在剩余的位置中的第 $L_i$ 个。
然后你会发现变成之后第一个存在的位置这个事情很容易做了,只需要把 $L_i$ 全部 $+1$ 就好。
不过这里要注意,如果 $L_i$ 在这一轮被删除了,那么是不需要 $+1$ 的。于是对于这一轮删除的位置,我们再单独把对应的询问扣掉就好。
$L_i$ 对应的询问有很多?显然当两个询问 $L_i$ 相等时,我们可以把他们直接合并,于是需要修改的询问数就是删除数,只会进行 $O(n)$ 次了。
使用线段树维护相对位置,并查集维护询问,总复杂度 $O(n\log n)$。
简单做法
首先可以发现,前缀是很好做的。如果固定 $l=1$,求每个 $r$ 的答案,和直接对全局做统计剩下多少 $[1,r]$ 的元素没有区别。
考虑怎么把区间转成前缀,容易想到前缀和相减,但是直接减肯定是有问题的。
一个重要的观察是,连续段从左边删还是从右边删,是完全等价的。
“前缀的后缀是区间”,我们可以把 $[l,r]$ 迭代 $k$ 次剩下多少,转化为对于 $[1,r]$ 每次删右边,操作 $k$ 次后剩下 $[l,r]$ 多少个元素。
此时就可以前缀和相减了,答案等于 $[1,r]$ 每次删右边 剩下多少 $[1,r]$ 的元素 减去 $[1,r]$ 每次删右边 剩下多少 $[1,l-1]$ 的元素。
由于前着操作范围和查询范围一致,我们再改成每次删左边,这就是最开始提到的前缀问题了。
而后者注意到若 $[l,r]$ 剩下了元素,则答案等于 $[1,n]$ 每次删右边 剩下多少 [1,l-1] 的元素,因为 $r$ 右边的位置被 $[l,r]$ 挡住了,对于 $[1,l]$ 来说全局和前缀没有区别。
而若 $[l,r]$ 没有剩下元素,后者算出来一定比真实值要大,于是减出来必然 $\le 0$,所以答案直接就是两个式子相减对 $0$ 取 $\max$。
对两边分开做,复杂度仍然是 $O(n\log n)$。
我的做法
这个做法有一些神秘的观察,我暂时不知道有没有更好的解释。
一开始我看错题了,以为是求连续段数,因此题解前半段是对着这个问题写的。
还是考虑全局与区间的关系,不难发现迭代 $k$ 次之后,全局的每个连续段,都对应着初始序列的一段区间。
这样的连续段总共只有 $O(n)$ 个,我们可以将其全部预处理出来,每个连续段的信息都形如,对应原序列 $[l,r]$ 的位置,且存在时间为 $[tl,tr]$。
考虑一次查询 $[ql,qr]$,对于 $ql\le l,r\le qr$ 且此时存在的连续段,显然在区间查询中这个连续段也必然存在。由于同一时刻连续段不交,这是一个动态一维数点,用 bit 维护一下就好。
比较麻烦的是,会有至多两个区间与 $[ql,qr]$ 有交,但不被 $[ql,qr]$ 完全包含,在区间查询中我们不知道这两个区间对应的连续段是否仍然存在。
于是我们现在的问题就是,给定 $2q$ 个询问,要求判断 $[l,r]$ 操作 $k$ 轮之后是否会删空。
试了一些 DS 常用手法,但是都不太能做,这个信息实在是没有办法合并。于是我们考虑最基础的问题,如果 $l=1$ 固定,怎么对于每个前缀求 $[1,r]$ 最小删空时间。
稍加思考之后不难发现,我们可以维护 $[1,r-1]$ 每个时刻末尾最后一个字符的颜色。当 $r-1\to r$ 时,找到第一个末尾颜色与 $s_r$ 不同的时刻 $p$,在 $[1,p-1]$ 这些时刻中 $r$ 都不会被删除,而在 $p$ 这个时刻恰好会被删除。同时,$p$ 时刻序列末尾的字符就会被改成 $s_r$ 了。
而如果 $p$ 不存在,那么说明整个前缀存在时间又变长了,所以把 $s_r$ 放在序列末尾即可。
于是对于判定一个区间 $[l,r]$ 是否会删空,我们可以形式化的描述成这样一个 DS 问题:
有一个初始全为 $-1$ 的无限长数组,以及 $0,1$ 两种操作类型,$0$ 操作会把序列第一个 $1/-1$ 改成 $0$,$1$ 操作会把序列第一个 $0/-1$ 改成 $1$,求所有操作进行完之后最后一个不是 $-1$ 的元素位置。
看起来很难做,但是一个很惊人的观察是,当且仅当序列全部同色,且再 push 一个这种颜色字符时,序列非 $-1$ 长度会增加。
如果把 $1$ 视作 $1$,$0$ 视作 $-1$,最大前缀和为 $Max$,最小前缀和为 $Min$,$1$ 对答案的贡献就是 $Max$,$0$ 对答案的贡献就是 $-Min$!
因此只要 $Max-Min>k$,区间就不会删空,否则必然被删空。
于是只要支持静态查询区间前缀和最大值/最小值,直接倍增维护一下就好。
但是这个题不是问连续段数,而是问剩余字符数,又该怎么办呢?
对于完全被 $[ql,qr]$ 包含的连续段,显然也是好做的,因为剩余字符数是一个一次函数,维护常数和一次项和即可。
而对于没被完全包含的区间,好像我们刚才的办法只支持判定,并不支持查询字符数,有点倒闭。
但细想一下,发现查询是有性质的:查询区间如果剩下了字符,则必然只剩下一种字符,换句话说这些字符构成恰好一个同色连续段。
这是因为我们查询的区间被包含在全局的某个同色连续段,在原序列中对应的区间中了。
这个同色连续段会在时刻 $Max-Min$ 删空,而每迭代一次恰好删除一个字符,所以剩余字符数等于删空所需要的迭代次数,也就是 $\max(0,Max-Min-k)$。
于是仍然倍增支持静态区间最小/最大前缀和查询,总复杂度 $O(n\log n)$。