2021 牛客暑期多校训练赛5 K - King of Range
题目链接: King of Range
题意
给定一个数列 a n a_n an和 m m m次询问,每次询问数列 a n a_n an中,区间范围(最大值-最小值)严格大于 k k k的的区间的个数
分析
由于大于k的区间不太好求,将题意转化求 所有区间个数 - 差值大于K的区间个数,统计差值小于K的区间的个数可用尺取+单调队列做
具体做法为,分别用一个单调递增队列维护一个最小值,和一个单调递减队列维护一个最大值
用 l , r l,r l,r两个游标进行尺取,每次向右移动 r r r扩展区间,同时将 r r r入队(两个队列),当前区间 ∣ 最 大 值 − 最 小 值 ∣ ≤ k |最大值-最小值| \leq k ∣最大值−最小值∣≤k 时,则以 r r r结尾的当前区间的所有子区间都均满足 ∣ 最 大 值 − 最 小 值 ∣ ≤ k |最大值-最小值| \leq k ∣最大值−最小值∣≤k,因此对答案的贡献为当前区间长度
当前区间 ∣ 最 大 值 − 最 小 值 ∣ > k |最大值-最小值|>k ∣最大值−最小值∣>k时, 则向右移动 l l l缩小区间并不断使 l l l出队直至 ∣ 最 大 值 − 最 小 值 ∣ ≤ k |最大值-最小值| \leq k ∣最大值−最小值∣≤k为止,再计算贡献
AC代码
#include <bits/stdc++.h>
typedef long long LL;
const int N = 1e5 +5;
int a[N];
std::deque<int> mx, mn;
void push(int x) {
while(!mx.empty() && a[mx.back()] <= a[x]) mx.pop_back();
mx.push_back(x);
while(!mn.empty() && a[mn.back()] >= a[x]) mn.pop_back();
mn.push_back(x);
}
void pop(int x) {
if(mx.front() == x) mx.pop_front();
if(mn.front() == x) mn.pop_front();
}
void clear() {
while(!mx.empty()) mx.pop_front();
while(!mn.empty()) mn.pop_front();
}
int main() {
int n, m; scanf("%d%d", &n, &m);
for(int i = 0; i < n; ++i)
scanf("%d", &a[i]);
for(int i = 0; i < m; ++i) {
int k; scanf("%d", &k);
LL ans = 1ll * n * (n+1)/2;
for(int l = 0, r = 0; r < n; ++r) {
push(r);
int mxx = mx.front(), mnn = mn.front();
if(a[mxx] - a[mnn] <= k) {
ans -= r - l + 1;
}
else {
while(a[mx.front()] - a[mn.front()] > k)
pop(l++);
ans -= r - l + 1;
}
}
printf("%lld\n", ans);
if(i < m - 1) clear();
}
return 0;
}