Description
给定 \(n\),\(k\) 和一个长度为 \(n\) 的序列,求最长的最大值最小值相差不超过 \(k\) 的子段。
Constraints
\(0 \le k \le 2*10^9\),\(1 \le n \le 3*10^6\)
Solution 1
考虑暴力。
暴力枚举最后答案的长度,然后再暴力寻找。
时间复杂度\(O(n^3)\),超时。
Solution 2
发现最终答案满足二分性质。
二分最终答案长度,固定区间后,就类似于滑动窗口一题,利用单调队列来维护区间里的最大值、最小值。
时间复杂度为 \(O(nlogn)\),由于 \(n\) 较大,常数过大可能会被卡掉一个点。
Code
// by youyou2007 in 2022
#include
#include
#include
#include
#include
#include
#include
#include
#include
Solution 3
上一个算法太靠人品了,还要再优化
发现我们可以不用去先确定答案长度,而是在维护单调队列时求出
考虑维护两个单调队列,分别维护的是当前以 \(a[i]\) 结尾的不上升(最大值)、不下降(最小值)的子序列,子序列中每个数的下标
在每一个位置判断一下这两个队列头位置下标所对应值的差是否大于k,如果大于,则将这两个队列中队头位置靠前(为了保持合法区间尽量大)的不断弹出,直到差值 \(≤k\)。
然后更新一下合法序列的左端点,即为弹出那个位置的后一个位置。每次更新一下序列长度的最大值就行了。
至于为什么要用不上升的维护最大值,不下降的维护最小值,其实也很好理解:
如果当前区间差值过大,说明最大值过大或最小值过小,为了减小差值,可以缩小最大值或扩大最小值。所以队列维护的都是可以缩小差值的元素。
时间复杂度 \(O(n)\)。
Code:
// by youyou2007 in 2022
#include
#include
#include
#include
#include
#include
#include
#include
#include