CF997E Good Subsegments

简要题意

一个好区间是其中数在值域上连续的区间,给定 \(n\) 的排列,每次给定一个区间,问其中有多少好的子区间。

数据范围:\(1\le n\le 120000\)

做法

只有整体询问的版本是 Cupboard Monsters。值域上连续当且仅当区间最大值减最小值等于区间长度,考虑维护最大值减最小值减区间长度,那么单调栈维护一下前面的最大、最小值,每次弹栈或压栈时线段树上区间加一下,移动右指针时把前面整体减一。这样保证所有数都非负,而我们要查询的是 \(0\) 的个数,则维护区间最小值、最小值个数即可。

区间询问考虑扫描线,把询问离线,按照右端点排序依次移动指针在线段树上做修改,则需要维护区间历史最小值个数。考虑在每个线段树节点维护两个值 \(sum, coef\),表示当前区间的历史最小值个数,子区间要累积上当前区间最小值个数的系数(也就是一种 tag)。每一次移动右端点到 \(i\) 后,给区间 \([1, i]\)\(coef\) 增加 \(1\),称为区间记录操作。下传时首先传递区间加 tag,然后判断左右子树最小值是否等于当前区间最小值,如果是就把 \(coef\) 传给它。

这样做为什么对?其实并不是那么显然的。考虑我们的 \(coef\) 标记会被传递到哪些节点。如果一个节点被区间记录操作访问到,我们很容易判断其当前最小值是不是 \(0\),并对正确的区间予以计算,并且这个标记被传递到的节点一定当前最小值为 \(0\),所以很正确。当我们进行了若干次其他操作以后进行下传时,被下传的区间可能已经被整体加减了,但此时的 \(coef\) 标记说明它在曾经最小值还是 \(0\) 时本应被统计到过,只是当时没有访问下来,那么由于整体加减只会影响最小值的数值,不会改变其分布,所以这时无论该区间当前最小值是不是 \(0\),直接按照左右子节点最小值的关系进行下传依然是正确的。区间加进行时所有涉及到的节点的标记都下传干净了,所以也不会造成问题。

真是很不懂数据结构,加训!

posted @ 2023-08-31 20:35  kyEEcccccc  阅读(41)  评论(1编辑  收藏  举报