Codeforces 1550C Manhattan Subarrays
借着这道题总结一下CF解题自己的一个小套路:观察性质,套数据范围,利用性质一步步推下一个子问题的做法。
观察性质
性质1
只有一个点被完全夹在了两个点中间时,才会出现bad triples。
即出现一个点(p, q),存在点(x1, y1)与(x2, y2)使得 p 属于 [x1, x2] 且 q 属于[y1, y2]。
性质2
每次新加入的点一定是当前所有点中x最大的。于是要找到两个点,它们的x递减,y要么递增,要么递减。
性质3
由性质3即得,一个区间是bad的 iff 存在一个单调序列 len >= 3。
套数据范围想做法
想法1
数据范围是2e5。利用two pointers,设每次区间查询复杂度为x,那么总体的算法复杂度是O(nx)的。这里要求O(x) <= lgx。那么需要找到一种lgx以内的做法。
第一步先验证two pointers的可行性。
首先想到,对于任意的[l, r],如果它是good的,[l + 1, r]一定是good的,我们没有必要计算这部分,这时候让r ++即可,直到[l, r]是一个bad的区间。
当发现[l, r]开始bad的时候,不断让l ++,获得一个good区间,继续迭代。
每次发现bad区间的时候,记录上一次good的区间[l, r]。这个区间[l, r]中的任意连续子区间都是good的。
对于重叠的部分是会重复计算的,需要设置一个last_l 与 last_y 去掉重复的部分。
two pointers看来是可行的。
第二步找到一个O(lgn)以内查询一个区间LIS长度的方法。
首先,线段树是可以实现的,但是代码太难实现。
其次,想到了两种单调的数据结构,分别是单调栈和单调队列。
但是这两种数据结构能解决的问题并不适合这道题。
单调栈可以用来查询数组中每个ai左边或者右边第一个比它大或者小的数值。
单调队列可以nlgn查询一个静态数组的LIS。缺点是不能动态。
单调队列还可以用来查询滑动窗口的最大值。缺点是不能求LIS。
//复习单调栈、单调队列、线段树
想法2
归纳一般性质。
容易发现,len >= 5的子区间其LIS长度一定>=3。
问题转化成判断所有长度为3或者4的子区间是否good即可。