处理带限制的点
问题描述
很多 DS 问题最终会转化成一个很基本的问题:点的问题。 常见的是:统计点数(能差分)/最优化点权(不能差分)。 同时,问题会对合法的点进行抽象定义。 那么,问题的难点就在于将抽象的合法限制以优的复杂度实现。
常识
- 静态的 k 维问题的复杂度一般为 $log^{(k - 1)}$。
- 动态的 k 维问题的复杂度一般为 $log^k$。
整体思路
核心:用尽可能少的 $log$ 刻画出限制。 有时,你容易提出一个 $log$ 特别多的限制办法,这时重点就是想办法减少限制。
对限制的具体处理思路
处理修改
- 将时间维看成一维限制处理:查询看成是对时间维的前缀修改点查询。
- 直接在线修改处理。
处理偏序(单限制不等式)
- 离线:扫描线(即 CDQ 的一维形态),CDQ 分治。离线带修可以将时间维看作一维限制。
- 在线不带修:序列上可持久化结构(例如主席树)。
- 在线带修:树状数组。
处理一般不等式
- 尝试套用偏序做法:
- 可以差分:差分后套偏序做法。
- 扫描线可以结合带修 DS 使用。
- 一般的:分治套偏序做法。
- 树套树:
- 一维形态:线段树。
- BIT 套 SGT:一维偏序,一维双向。
- SGT 套 SGT(二维线段树):两维双向。
- K - D tree
注意
- 优先考虑离线做法,在线做法时空比较劣。
- 并不一定要用时间维分析,因为时间维将修改操作一般化了,失去了特殊性。
处理限制的思考技巧
- 一般化表示比较复杂,可以简单分类讨论:情况变多了,但单个情况的条件变少了,去除了冗余条件。
- 灵活转化/分裂/合并偏序限制和一般限制:
- 一般限制可以拆分成上下界的偏序限制。特别的,可差分的问题可以直接去掉一般限制。
- 有时,多个偏序限制可以合并成一个一般限制。
- 询问和点贡献方式互换:有时询问贡献点更好处理,就要交换。
- 其他的思考技巧就和分析性质相似了。
典例
区间 mex
- 显然二分,问题就变成:$<=lim$ 的所有数是否都在 $[l, r]$ 中出现。
- 出现可以刻画成:对于 $x$ 的所有出现位置 $pos$,存在 $l <= pos <= r$。
- 拆偏序处理,那么 $pos <= r$ 直接扫掉,条件就转化成 $max(pos) >= l$。
给定若干区间 $[l_i, r_i]$ 以及对应权值 $c_i$,多次询问和 $[l, r]$ 的交 $>= k$ 的区间的最小权值
- 直接分析的话可以列出一堆条件,非常麻烦,考虑减少条件数。
- 有交这个一般条件刻画复杂,考虑分类讨论:
- 完全包含,完全被包含,包含左端点,包含右端点。
- 完全包含可以被最后两个替代,只剩三种情况。
- 最后两个都是好做的,即二维偏序。
- 完全被包含需要对长度和左右端点做限制,即:$l <= l_i$,$r_i <= r$,$r_i - l_i + 1 >= k$。
- 这并不足以通过,考虑再次优化条件。
- 发现前两个偏序可以合并成对 $r_i$ 的双向限制,那么扫偏序再线段树处理双向限制即可。
区间查询前驱
- 问题刻画:
- 每个点 $(pos, val)$。
- $lim$:$l <= pos <= r$,$val <= x$。
- $aim$:$maximum(val)$。
- 离线静态做法:观察到限制形态,直接扫描掉偏序,剩下的用 SGT。
- 离线动态做法:
- 将时间维也刻画出来:每个点 $(t_l, t_r, pos, val)$,询问 $(t, l, r, x)$。
- $lim$:$t_l <= t <= t_r$,$l <= pos <= r$,$val <= x$。
- $aim$:$maximum(val)$。
- 一个办法是把一般不等式拆掉,应该是 $log^3$。
- 考虑直接树套树,扫 $val <= x$,剩下的线段树套线段树。
- 在线动态做法:树套树可以直接修改,这里将 $val <= x$ 作 BIT 那一维可能会更方便。
矩形加矩形查和
- 首先肯定差分,将矩形查差分成前缀矩形查(键值只有单点了)。
- 考虑一下矩形加的差分,本来想也这么差,但是不太好贡献,可以差成:后缀矩形加。
- 考虑一个点 $(x, y, v)$ 对一个询问 $(q_x, q_y)$:
- $lim$:$x <= q_x$,$y <= q_y$。
- 贡献:$(q_x - x + 1) * (q_y - y + 1) * v=( (q_x + 1) * (q_y + 1) - (x * (q_y + 1)+y * (q_x + 1)) + x * y ) * v$。
- 直接维护四个二维偏序即可,可以在线或离线,都是 $log$。
先做矩形加,然后做矩形查最值
- 限制是二维的双向不等式,而且求最值不能差分。
- 树套树显然没有前途,主要是 tag 不好处理。
- 可以考虑分治,每次处理跨过中线的询问,然后将右半部分去掉。
- 首先考虑偏序做法,显然可以差分以后历史最值,那么直接套用即可。
区间加、区间撤销上次加、单点查值($1e5$ 级别)
- 可以看成每个点都有一个栈,每次可以区间 push 和区间 pop,然后单点查栈中和。
- 线段树完蛋,看看根号做法,也不太行,因为撤销受限于时间顺序。
- 那么得转化一下,加上时间维。
- 但是注意操作有时间顺序,用不等式刻画有点问题,所以画出平面来考虑。
- 发现区间操作就是一条竖线,单点查值就是查横的线,从左到右看的。
- 考虑竖着扫,维护每个时间点对应的答案,每次查前缀。
- 容易发现这个可以类似括号匹配,区间维护已经匹配剩下的 ))))((((。
- 而右括号只要维护个数,左括号需要维护所有的值。
- 直接维护理论可行,但是还有单点修改以及前缀查,我们需要快速实现:
- 删掉一段后缀左括号。
- 拼接两段左括号。
- 整段查询左括号对应的权值和。
- 好像只能平衡树,而且每个点都要存,所以要可持久化。