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 nleq10^5 n105

题解

  • 这种题似乎很套路,类似的题不少。
  • 先假如没有重复的数,则所有区间满足 m a x − m i n ≥ r − l max-mingeq r-l maxminrl,当且仅当区间符合上述条件时等号成立,
  • 即当 m a x − m i n + l max-min+l maxmin+l的最小值为 r r r时等号成立,符合条件。
  • 那么可以用维护每个位置的 m a x − m i n + l max-min+l maxmin+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 maxmin+srl,其中 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
", ans);
	return 0;
}
哈哈哈哈哈哈哈哈哈哈
原文地址:https://www.cnblogs.com/LZA119/p/13910012.html