[Ynoi2016] 镜中的昆虫 题解
好题值得一做。
题目大意:给一个序列,有若干个离线询问,每次可以区间推平或询问区间内的颜色个数,数据范围是 \(10^5\) 级别。
解题思路:我们可以先考虑一个弱化版,每次是单点修改怎么做,类似于 CF848C。
我们考虑维护出每一个位置上一个与它相等的位置是 \(pre_i\),那么区间内数颜色就是求二维偏序 \(l\le i\le r\land pre_i<l\)。
我们又发现,每次单点修改只会有 \(O(1)\) 个位置的 \(pre\) 被修改,可以对每个颜色开一个 set
方便维护每次 \(pre\) 的变化。
之后我们就把修改和查询都离线下来,这样多了时间一维的限制,跑 cdq分治 即可,复杂度 \(O(n\log ^2n)\)。
考虑完弱化版,本题怎么做呢?可以发现即使是区间推平,\(pre\) 数组的总变化量是均摊 \(O(n+m)\) 的。
证明也很容易想,考虑到新推平的区间覆盖了原来的若干个区间,那么只有原来若干个区间的左端点的 \(pre\) 会改变,而区间的其他位置 \(i\) 由于与前面的颜色仍然相同,保持 \(pre_i=i-1\) 不变。而每个区间只会被覆盖一次,一共只有 \(O(n+m)\) 个区间。
当然还有这些被覆盖区间的相同颜色后继区间的左端点会被改变。以及 \(L,R+1\) 也可能改变。但总的还是 \(O(n+m)\)。
实现方面,不仅对每种颜色开一个 set
,里面存放这种颜色的每个区间,这样可以方便维护出贡献。还要对所有区间开一个 set
,同时存放颜色,这样可以方便地删除区间。
而且我们不要一边删除区间一边算贡献,先把所有可能会改变的点存起来,把它们的贡献去掉,之后修改 set
至当前状态,再把这些点的贡献加上。
对于 cdq分治 部分,不要把询问与修改放在同一个容器里,可以分治时间,把左半时间的修改和右半时间的询问分别放进 vector
里,然后分别排序,然后用双指针扫两个 vector
。是不是很类似整体二分的写法呢?
最后 \(pre\) 的变化量是 \(O(n)\) 级别的,用 set
处理是 \(O(n\log n)\),分治部分是 \(O(n\log ^2n)\)。总复杂度为 \(O(n\log ^2n)\)。