给一个长度为
n
n
n的序列,求有多少个区间
[
l
,
r
]
[l,r]
[l,r],使得区间排序后相邻两数之差小于
1
1
1.
n
≤
1
0
5
nleq10^5
n≤105
题解
这种题似乎很套路,类似的题不少。
先假如没有重复的数,则所有区间满足
m
a
x
−
m
i
n
≥
r
−
l
max-mingeq r-l
max−min≥r−l,当且仅当区间符合上述条件时等号成立,
即当
m
a
x
−
m
i
n
+
l
max-min+l
max−min+l的最小值为
r
r
r时等号成立,符合条件。
那么可以用维护每个位置的
m
a
x
−
m
i
n
+
l
max-min+l
max−min+l,
r
r
r不断往右移,再用单调栈维护
m
a
x
max
max和
m
i
n
min
min,弹栈时在线段树上修改并记录最小值及其个数,
最后查询最小值,若等于
r
r
r,则答案加上最小值的个数。
那有重复的数怎么办呢?
那么所有区间满足
m
a
x
−
m
i
n
+
s
≥
r
−
l
max-min+sgeq r-l
max−min+s≥r−l,其中
s
s
s是非首次出现的数的个数,那么直接预处理好每个数前面第一个和它相同的数的位置
l
a
s
t
last
last,
r
r
r向右移动时,将
[
1
,
l
a
s
t
r
]
[1,last_r]
[1,lastr]加上
1
1
1。
代码
#include<cstdio>#include<cstring>#include<algorithm>usingnamespace std;#define N 100010#define ll long longint a[N], la[N], A[N], B[N];struct node {int x, i;}b[N];struct S{int x, s;}f[N *4];int bz[N *4];intcmp(node x, node y){if(x.x == y.x)return x.i < y.i;return x.x < y.x;}voidupdate(int v){
f[v]= f[v *2];if(f[v *2+1].x < f[v].x) f[v]= f[v *2+1];elseif(f[v *2+1].x == f[v].x) f[v].s += f[v *2+1].s;}voiddown(int v){if(bz[v]){
f[v *2].x += bz[v], f[v *2+1].x += bz[v];
bz[v *2]+= bz[v], bz[v *2+1]+= bz[v];
bz[v]=0;}}voidmake(int v,int l,int r){if(l == r){
f[v].x =0,f[v].s =1;}else{int mid =(l + r)/2;make(v *2, l, mid),make(v *2+1, mid +1, r);update(v);}}voidadd(int v,int l,int r,int x,int y,int c){if(l == x && r == y){
f[v].x += c;
bz[v]+= c;}else{int mid =(l + r)/2;down(v);if(y <= mid)add(v *2, l, mid, x, y, c);elseif(x > mid)add(v *2+1, mid +1, r, x, y, c);elseadd(v *2, l, mid, x, mid ,c),add(v *2+1, mid +1, r, mid +1, y, c);update(v);}}
S find(int v,int l,int r,int x,int y){if(l == x && r == y)return f[v];int mid =(l + r)/2;down(v);
S t;if(y <= mid) t =find(v *2, l, mid, x, y);elseif(x > mid) t =find(v *2+1, mid +1, r, x, y);else{
t =find(v *2, l, mid, x, mid);
S tt =find(v *2+1, mid +1, r, mid +1, y);if(tt.x < t.x) t = tt;elseif(tt.x == t.x) t.s += tt.s;}update(v);return t;}intmain(){int n, i;scanf("%d",&n);for(i =1; i <= n; i++){scanf("%d",&a[i]);
b[i].x = a[i], b[i].i = i;}sort(b +1, b + n +1, cmp);for(i =1; i <= n; i++)if(i ==1|| b[i].x > b[i -1].x) la[b[i].i]=0;else la[b[i].i]= b[i -1].i;make(1,1, n);
ll ans =0;int sa =0, sb =0, s =0;for(i =1; i <= n; i++){add(1,1, n, i, i, i);while(sa && a[i]> a[A[sa]]){add(1,1, n, A[sa -1]+1, A[sa], a[i]- a[A[sa]]);
sa--;}while(sb && a[i]< a[B[sb]]){add(1,1, n, B[sb -1]+1, B[sb], a[B[sb]]- a[i]);
sb--;}
A[++sa]= i;
B[++sb]= i;if(la[i]){add(1,1, n,1, la[i],1);}
S t =find(1,1, n,1, i);if(t.x == i) ans += t.s;}printf("%lld
", ans);return0;}