• 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;
    }
    
    哈哈哈哈哈哈哈哈哈哈
  • 相关阅读:
    实验六
    派生类构造函数的理解
    实验五
    随机数
    面向行的输入(随时添加新的理解)
    实验四
    循序渐进VUE+Element 前端应用开发(21)--- 省市区县联动处理的组件使用(转载)
    循序渐进VUE+Element 前端应用开发(20)--- 使用组件封装简化界面代码(转载)
    循序渐进VUE+Element 前端应用开发(19)--- 后端查询接口和Vue前端的整合(转载)
    循序渐进VUE+Element 前端应用开发(18)--- 功能点管理及权限控制 (转载)
  • 原文地址:https://www.cnblogs.com/LZA119/p/13910012.html
Copyright © 2020-2023  润新知