陶陶摘苹果「线段树维护单调栈」

题目描述

题库后缀为contest/132/problem/2

思路分析

  • 线段树维护单调栈板子题
  • 然而学长说会做楼房重建,就会做这道题,更巧的是我在十几天前刚做了这道题,当时过了也不知道还有线段树维护单调栈这种东西……,不过思路确实和线段树维护单调栈是一致的。

关于线段树维护单调栈

  • 核心在于,在线段树中,其左儿子和右儿子会互相影响,这道题中显然就是右儿子会受到左儿子的影响
  • 目前本人比较偏向的板子中,核心是一个 calc 函数,传入两个参数——\(rt\)\(w\) ,\(w\) 代表 \(rt\) 节点所统领的区间所受到的限制的值。
  • 如果左儿子的最大值比 \(w\) 大,那么就递归左儿子,右儿子不必再递归,因为单调递增,右儿子的最小值一定大于等于左儿子的最大值。①
    否则递归右儿子,这时候左儿子一定不满足,而右儿子有可能满足。②
    还有就是到叶子节点的时候,直接和 \(w\) 比较一下就好了。③
  • 然后 pushup 的时候记得对右儿子调用 calc 函数(上面说的右儿子会受左儿子限制),以保证单调性

\(Code\)

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define R register
#define N 100010
using namespace std;
inline int read(){
	int x = 0,f = 1;
	char ch = getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
int n,m,h[N];
struct node{
	int mx,len,l,r;//mx为区间最大值,len满足单调性的长度。
}tr[N<<2];
#define ls rt<<1
#define rs rt<<1|1
int calc(int rt,int w){
	if(tr[rt].l==tr[rt].r)return tr[rt].mx > w;//③
	if(tr[ls].mx>w)return calc(ls,w)+tr[rs].len;//①
	else return calc(rs,w);//②
}
void pushup(int rt){
	tr[rt].mx = max(tr[ls].mx,tr[rs].mx);
	tr[rs].len = calc(rs,tr[ls].mx);//左儿子和谐右儿子
}
void build(int rt,int l,int r){
	tr[rt].l = l,tr[rt].r = r;
	if(l==r)return tr[rt].mx = h[l],void();
	int mid = (l+r)>>1;
	build(ls,l,mid);
	build(rs,mid+1,r);
	pushup(rt);
}
void modify(int rt,int x){
	if(tr[rt].l==tr[rt].r)return tr[rt].mx = h[tr[rt].l],void();
	int mid = (tr[rt].l+tr[rt].r)>>1;
	if(x<=mid)modify(ls,x);
	else modify(rs,x);
	pushup(rt);
}
int main(){
#if 1
	freopen("taopapp.in","r",stdin);
	freopen("taopapp.out","w",stdout);
#endif
	n = read(),m = read();
	for(R int i = 1;i <= n;i++)h[i] = read();
	build(1,1,n);
	for(R int i = 1;i <= m;i++){
		int pos = read(),height = read();
		int pre = h[pos];
		h[pos] = height;
		modify(1,pos);
		printf("%d\n",calc(1,-1));
		h[pos] = pre;
		modify(1,pos);
	}
	return 0;
}

posted @ 2020-10-12 08:49  HH_Halo  阅读(154)  评论(2编辑  收藏  举报