【CF1672I】PermutationForces(线段树)

\(c_i=|i-p_i|\)。可以证明,删掉一个 \(c_i\leq s\) 的点后,只会让 \(c_j>s\) 的点的 \(c_j\) 变小,且原本 \(c_j\leq s\) 的点的 \(c_j\) 仍不会大过 \(s\)。也就是说我们每次能随便删掉一个 \(c_i\leq s\) 的点来判断合法性。

但这样每次删除后的变化都得 \(O(n)\) 处理,那么只能得到一个 \(O(n^2\log n)\) 的做法。考虑找一种更好的删除顺序。

发现若每次我删除 \(c_i\) 最小的点,那么其他点的 \(c_j\) 都不会变大只会变小。放到平面上,操作就是 \((i,p_i)\) 左上角和右下角的点的 \(c_j\) 减一,然后再把 \((i,p_i)\) 删掉。这个过程可以用 KDT 来实现做到 \(O(n\sqrt n)\) 的复杂度(你发现 \(s\) 也不用二分了,只需对每次删除的 \(c_i\)\(\max\) 即可)。

注意到,由于我们要找的是离 \(y=x\) 最近的那些点,所以我们可以暂时只维护左上部分点的右下凸壳和右下部分点的左上凸壳,这样一次操作就变成了区间加可以一 log 维护。

如何找到新加入的点?可以把凸壳改为前缀/后缀最值,这样就容易用线段树维护了。

新加入的点的权值如何计算?这看起来是个二维数点问题只能双 log 做,但实际上结合回原来的题意可以直接改回 \(x>A\)\(y>B\) 减,这就能一 log 维护了。

总时间复杂度 \(O(n\log n)\)

这个套路其实是第二次见了,第一次见是在,结果现在还是不会……

posted @ 2022-12-31 22:06  ez_lcw  阅读(55)  评论(0编辑  收藏  举报