JZOJ 6808. 【2020.10.29提高组模拟】easy(单调栈+线段树)
JZOJ 6808. 【2020.10.29提高组模拟】easy
题目大意
- 给一个长度为 n n n的序列,求有多少个区间 [ l , r ] [l,r] [l,r],使得区间排序后相邻两数之差小于 1 1 1.
- n ≤ 1 0 5 n\leq10^5 n≤105
题解
- 这种题似乎很套路,类似的题不少。
- 先假如没有重复的数,则所有区间满足 m a x − m i n ≥ r − l max-min\geq 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+s\geq 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>
using namespace std;
#define N 100010
#define ll long long
int 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];
int cmp(node x, node y) {
if(x.x == y.x) return x.i < y.i;
return x.x < y.x;
}
void update(int v) {
f[v] = f[v * 2];
if(f[v * 2 + 1].x < f[v].x) f[v] = f[v * 2 + 1]; else if(f[v * 2 + 1].x == f[v].x) f[v].s += f[v * 2 + 1].s;
}
void down(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;
}
}
void make(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);
}
}
void add(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);
else if(x > mid) add(v * 2 + 1, mid + 1, r, x, y, c);
else add(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);
else if(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; else if(tt.x == t.x) t.s += tt.s;
}
update(v);
return t;
}
int main() {
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\n", ans);
return 0;
}
哈哈哈哈哈哈哈哈哈哈