【牛客竞赛】King of Range题解


题目大意

给定数组 $a_i$,$q$ 次询问,每次询问是一个非负整数 $k$,求出有多少对 $(l, r)$,满足 $\max(a[i]) - \min(a[j]) > k$,其中 $l \le i, j \le r$。

题目链接

思路

不难发现满足要求的序列有单调性,即如果当前区间满足最大值减去最小值大于k,那么包含这个区间的更大的区间,也一定满足

而这一类问题,通常可以采用尺取法。即:

  • 我们先固定起点 $l$,然后让 $r$ 从 $l$ 开始一个一个往后走,并记录最大值与最小值。
  • 一旦当前序列满足了条件,那么选取 $r$ 之后的点作为结尾,也一定会满足,所以直接让答案 $ans\ \mathrel{+}= n - r + 1$,然后让 $l$ 后移动一位。
  • 因为 $r$ 在恰好满足条件的时候就不再移动了,所以 $l$ 在往后移动一位的时候,满足条件的结尾一定在 $r$ 点或之后,所以 $r$ 的起点还是当前位置,然后继续按照第一步继续,直到统计完成。
  • 因为我们需要记录区间的最大值和最小值,所以我们可以用两个单调队列来统计,即统计最大值的时候,如果新加入的数 $X$ 比当前的数 $Y$ 要大,那么就让 $Y$ 出队,因为$X$ 比 $Y$ 大还比它靠后,意味着在取最大值的时候,只要有 $X$ 在,就永远不会取到 $Y$,而 $X$ 又在 $Y$ 之后,也就是说它出队也晚于 $Y$,所以 $Y$ 就没有存在的必要了(惨 Y 惨)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <cstdio>
#include <iostream>
#include <deque>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxN = 1e5 + 7;
typedef long long ll;

int m, n, a[maxN];

ll calc(ll k)
{
deque<int> maxPos, minPos;
int l = 1,r = 0;
ll ans = 0;
while(l <= n && r <= n) {
while((!maxPos.empty()) && maxPos.front() < l)
maxPos.pop_front();
while((!minPos.empty()) && minPos.front() < l)
minPos.pop_front();
if((!maxPos.empty()) && a[maxPos.front()] - a[minPos.front()] > k) {
ans += n - r + 1;
++l;
continue;
}
else {
++r;
while(!maxPos.empty() && a[maxPos.back()] <= a[r])
maxPos.pop_back();
maxPos.push_back(r);
while(!minPos.empty() && a[minPos.back()] >= a[r])
minPos.pop_back();
minPos.push_back(r);
}
}
return ans;
}

int main()
{
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
for(int i = 1; i <= m; ++i) {
int k;
scanf("%d", &k);
printf("%lld\n", calc(k));
}
return 0;
}

Author: BY 水蓝
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source BY 水蓝 !
  TOC