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\),直接按照左右子节点最小值的关系进行下传依然是正确的。区间加进行时所有涉及到的节点的标记都下传干净了,所以也不会造成问题。
真是很不懂数据结构,加训!