https://ac.nowcoder.com/acm/contest/11256/K
解法1: ST表 + 双指针
using namespace std;
int mx[100010][25], mn[100010][25], n, m, a[100010], cc[100010], k;
void rmq_init(){
for (int j = 1; (1 << j) <= n; j++) {
for (int i = 1; i + (1 << j) - 1 <= n; i++) {
mn[i][j] = min(mn[i][j-1], mn[i + (1 << (j-1))][j-1]);
mx[i][j] = max(mx[i][j-1], mx[i + (1 << (j-1))][j-1]);
}
}
// 小于等于len的最大的2的k次幂
for (int len = 1; len <= n; ++len) {
int k = 0;
while ((1 << (k + 1)) <= len){
k++;
}
cc[len] = k;
}
}
int rmq_min(int L, int R){
int kk = cc[R-L+1];
return min(mn[L][kk], mn[R-(1<<kk)+1][kk]);
}
int rmq_max(int L, int R){
int kk = cc[R-L+1];
return max(mx[L][kk], mx[R-(1<<kk)+1][kk]);
}
int main(){
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
mn[i][0] = a[i];
mx[i][0] = a[i];
}
rmq_init();
long long res;
while (m--){
scanf("%d", &k);
int l, r = 1;
res = 0;
for (l = 1; l <= n; ++l) {
while(r <= n && rmq_max(l, r) - rmq_min(l, r) <= k){
r++;
}
if(rmq_max(l, r) - rmq_min(l, r) > k){
res += (n - r + 1);
}else{
break;
}
}
printf("%lld\n", res);
}
return 0;
}
解法二: 单调队列
可以发现,实际要找的区间(l
, r
)的l
是从1, 2....x
,逐渐上升的,x
代表:以(x + 1
)区间为起点时,r
向右扩展(直到r
⇒ n
),在这个区间也找不出满足条件的两个数,并且我们可以发现,当找到一个离l
最近的两个满足条件的数时,这两个满足条件的数的较右边的数的位置为r
,对于(l
, r
)区间,当l
不变拓展r向右时(直到r == n
),这些区间一定也满足条件,所以,以l
为起点的区间,满足条件的有n - r + 1
个,所以现在要求的是,离l
最近的满足条件的两个数。利用单调队列的性质,固定r,每次找到两个数(ai
, aj
)(i < j
,并且ai
是离ar
最近的),此时(l
, j
)满足上面说的区间特性,所以res += (n - j + 1)
l++
,如果此时l <= i
,说明(l
, r
)任然包含(ai
, ar
),所以继续有res += (n - j + 1)
,直到l > i
,并在单调队列弹出 i
。
而对于利用单调队列找满足条件的(ai
,aj
),可以看成是固定j
的位置,找到离j
最近的满足i
的位置 ! ! ! ! ! !
---------------------------------------------------------------
而对于mx
数组,维护一个单调递减队列,当当前值比mx[t]
(队尾元素)大于等于时,明显:当前元素对于等会的离 j
最近的元素的贡献更好(会更靠近j
,并且对差值的贡献更大),所以弹出队尾元素。
---------------------------------------------------------------
对于mi
数组,维护一个单调递增队列,当当前值比mi[t]
(队尾元素)小于等于时,明显:当前元素对于等会的离 j 最近的元素的贡献更好(会更靠近j
,并且对差值的贡献更大),所以弹出队尾元素。
另一种思路可以参考此佬写的题解
using namespace std;
const int N = 1e5 + 5;
int n, m, k, a[N], mi[N], mx[N];
int main(){
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
}
while (m--){
scanf("%d", &k);
int h1 = 1, t1 = 0, h2 = 1, t2 = 0;
long long ans = 0;
int ll = 1;
for (int i = 1; i <= n; ++i) {
while (h1 <= t1 && a[i] >= a[mx[t1]]){
t1--;
}
mx[++t1] = i;
while (h2 <= t2 && a[i] <= a[mi[t2]]){
t2--;
}
mi[++t2] = i;
while (a[mx[h1]] - a[mi[h2]] > k){
ans += (n - i + 1);
ll++;
if(mx[h1] < ll){
h1++;
}
if(mi[h2] < ll){
h2++;
}
}
}
printf("%lld\n", ans);
}
return 0;
}