陶陶摘苹果「线段树维护单调栈」
题目描述
题库后缀为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;
}