题意:给一个序列,求有多少个连续子区间的中的最大值减最小值>k。
最开始做ST表,但感觉铁TLE(但是看有群友是ST表做出来的,不知道用了什么优化)
后来做法是双指针+单调队列。
保证单调队列的方法:
=================
a[n]储存每个数
qMax,qMin 单调队列,其中qMax.front()是当前最大数的下标,qMin.front()是当前最小数的下标.
固定L=1,然后不断R++,如果a[R]要大于a[qMax.back()],就不断弹出qMax的队尾,弹到qMax为空或者a[R]小于此时的a[qMax.back()]随后就push_back( R ),这样可以保证单调队列.qMin的操作同理
==============================
当a[qMax.front()] - a[qMin.front()] > k
时,表明所有包含了L到R这个小区间的大区间都能满足题目要求,所以[L,R],[L,R+1],[L,R+2]一直到[L,n]
这些区间都满足,所以ans += n - r + 1;
这时我们再L++,如果移动后qMax.front的编号是L(qMax储存的是队列中最大值的编号),就要qMax.pop_front();
。随后再进行一次a[qMax.front()] - a[qMin.front()] > k
的判断,如果成立就能再一次ans += n - r + 1;
,对于qMin的操作同理.直到判断不再成立再继续移动R
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
int a[maxn];
int main() {
int n, m, k;
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
while (m--) {
cin >> k;
deque<int> qMax, qMin;
long long ans = 0;
for (int l = 1, r = 1; r <= n; ++ r) {//保证整个队列要单调
while (qMax.size() && a[qMax.back()] < a[r])//只要qmax最尾部的数小于a[r],就不断弹出
qMax.pop_back();
qMax.push_back(r);//然后将r放进去
while (qMin.size() && a[qMin.back()] > a[r])
qMin.pop_back();
qMin.push_back(r);
while (a[qMax.front()] - a[qMin.front()] > k) {
ans +=(long long) n - r + 1;//只要包含了l到r的区间都成立
l ++;
if (qMax.front() < l)//向右移动l,如果队列最大值的编号是l就弹出
qMax.pop_front();
if (qMin.front() < l)
qMin.pop_front();
}
}
printf("%lld\n", ans);
}
}