一个区间合法当且仅当 , 移项得 ,
可以想到枚举 , 记录左边第一个比 小的位置和右边第一个比 小的位置,
分别记为 , 然后在 中枚举 左端点属于 , 右端点属于 ,
直接裸算时间复杂度期望 ,
加上枚举右端点时的 “可行性剪枝”, 和对 连续数字的加速, 跑随机数据 可出答案 .
从左向右 枚举右端点 , 检查 的左边有多少 满足 ,
其中 都是在 区间意义下的,
- 这里有个 性质: .
有了这个, 就可以考虑使用 线段树 维护 的 区间最小值,
因为只有可能 最小值 满足 .
考虑 向右移动一位对 的影响,
此时 可能成为关于 的新的 最大值, 也可能成为新的 最小值,
而影响的区间又是连续的, 考虑使用 单调栈 维护,
: 以维护 最大值 为例, 建立一个 单调递减 的 单调栈, 栈 中存下标,
在弹出 栈顶 时在 线段树 中更新 栈顶 与 次栈顶 的 差 大小的 左端点们 代表的区间 .
单调递增 的 单调栈 也按此方法维护 最小值 .
对于 , 统计 线段树 的区间 中有多少 最小值 等于 即可 .
#include<bits/stdc++.h>
#define reg register
int read(){
char c;
int s = 0, flag = 1;
while((c=getchar()) && !isdigit(c))
if(c == '-'){ flag = -1, c = getchar(); break ; }
while(isdigit(c)) s = s*10 + c-'0', c = getchar();
return s * flag;
}
const int maxn = 300005;
int N;
int top[2];
int A[maxn];
int stk[2][maxn];
struct Segment_Tree{
struct Node{ int l, r, tag, min_v, min_n; } T[maxn<<3];
void Push_up(int k){
T[k].min_v = std::min(T[k<<1].min_v, T[k<<1|1].min_v);
T[k].min_n = 0;
if(T[k].min_v == T[k<<1].min_v) T[k].min_n += T[k<<1].min_n;
if(T[k].min_v == T[k<<1|1].min_v) T[k].min_n += T[k<<1|1].min_n;
}
void Build(int k, int l, int r){
T[k].l = l, T[k].r = r;
if(l == r){ T[k].min_v = l, T[k].min_n = 1; return ; }
int mid = l+r >> 1;
Build(k<<1, l, mid), Build(k<<1|1, mid+1, r), Push_up(k);
}
void Push_down(int k){
T[k].min_v += T[k].tag;
int lt = k << 1, rt = k<<1 | 1;
T[lt].tag += T[k].tag, T[rt].tag += T[k].tag;
T[k].tag = 0;
}
void Modify(int k, const int &ql, const int &qr, const int &v){
int l = T[k].l, r = T[k].r;
if(T[k].tag) Push_down(k);
if(l > qr || r < ql) return ;
if(ql <= l && r <= qr){ T[k].tag += v, Push_down(k); return ; }
int mid = l+r >> 1;
Modify(k<<1, ql, qr, v), Modify(k<<1|1, ql, qr, v);
Push_up(k);
}
int Query(int k, const int &ql, const int &qr){
int l = T[k].l, r = T[k].r;
if(T[k].tag) Push_down(k);
if(ql <= l && r <= qr){ return T[k].min_v==qr?T[k].min_n:0; }
int mid = l+r >> 1, s = 0;
if(ql <= mid) s += Query(k<<1, ql, qr);
if(qr > mid) s += Query(k<<1|1, ql, qr);
return s;
}
} seg_t;
int main(){
N = read();
for(reg int i = 1; i <= N; i ++) A[i] = read();
seg_t.Build(1, 1, N);
long long Ans = 0;
for(reg int r = 1; r <= N; r ++){
while(top[0] && A[stk[0][top[0]]] > A[r]){ // 1, 2, 3
seg_t.Modify(1, stk[0][top[0]-1]+1, stk[0][top[0]], A[stk[0][top[0]]] - A[r]);
-- top[0];
}
stk[0][++ top[0]] = r;
while(top[1] && A[stk[1][top[1]]] < A[r]){
seg_t.Modify(1, stk[1][top[1]-1]+1, stk[1][top[1]], A[r] - A[stk[1][top[1]]]);
-- top[1];
}
stk[1][++ top[1]] = r;
Ans += seg_t.Query(1, 1, r);
}
printf("%lld
", Ans);
return 0;
}