Weed「线段树」
Posted hhhhalo
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Weed「线段树」相关的知识,希望对你有一定的参考价值。
题目描述
后缀为 contest/140/problem/3
思路分析
- 考场上的确写了线段树,但是是针对中间那 (20) 分打的……
- 其实这道题的思想和线段树维护单调栈非常类似,(
如果不明白建议去看一下这篇博客陶陶摘苹果)都是左右儿子之间互相和谐,只不过维护单调栈时左右儿子互相和谐的原因是因为要满足单调性,而这道题已经给出了和谐的条件 - 同样的,这题的难点也在于
pushup
合并。为了方便维护和合并区间,我们需要在线段树中记录三个值:(cnt)—添加的层数,(val)—添加的高度,(cut)—需要删除的层数。
这样在pushup
时,分三种情况讨论:
1. 右儿子将左儿子区间内的层数全部删除完,这时候只剩下右儿子
2. 右儿子没有删除操作,这时候直接讲将左右儿子信息合并即可
3. 左儿子未被右儿子删除完,这时候需要用一个函数来判断左儿子还剩下多少 - 定义这个函数为
find
,用来进行删除,显然我们要从右儿子开始删除,因为是从右向左删,这时候同样分为三种情况:
1. 右儿子刚好被删除,直接删就好了
2. 右儿子没有被删完,继续递归右儿子处理
3. 右儿子被删完后左儿子也会被删,这时候直接递归左儿子,因为右儿子删没了
(Code)
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define R register
#define N 400010
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 m,q,c[N],v[N];
struct Segment_Tree{
int cnt,val,cut;
}tr[N<<2];
#define ls rt<<1
#define rs rt<<1|1
int find(int rt,int w){//以此对应上面的三种情况
if(tr[rs].cnt == w)return tr[rt].val - tr[rs].val;
if(tr[rs].cnt > w)return tr[rt].val - (tr[rs].val - find(rs,w));
else return find(ls,w+tr[rs].cut-tr[rs].cnt);
}
void pushup(int rt){//同样
if(tr[rs].cut>=tr[ls].cnt){
tr[rt].cut = tr[ls].cut + tr[rs].cut - tr[ls].cnt;
tr[rt].cnt = tr[rs].cnt,tr[rt].val = tr[rs].val;
}
else if(!tr[rs].cut){
tr[rt].cut = tr[ls].cut;
tr[rt].cnt = tr[ls].cnt + tr[rs].cnt,tr[rt].val = tr[ls].val + tr[rs].val;
}
else{
tr[rt].cut = tr[ls].cut;
tr[rt].cnt = tr[rs].cnt + tr[ls].cnt - tr[rs].cut;
tr[rt].val = tr[rs].val + find(ls,tr[rs].cut);
}
}
void build(int rt,int l,int r){
if(l==r){
if(!c[l])tr[rt].cnt = 1,tr[rt].val = v[l],tr[rt].cut = 0;
else tr[rt].cnt = 0,tr[rt].val = 0,tr[rt].cut = v[l];
return;
}
int mid = (l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
pushup(rt);
}
void modify(int rt,int l,int r,int aim,int flag,int val){
if(l==r){
if(!flag)tr[rt].cnt = 1,tr[rt].val = val,tr[rt].cut = 0;
else tr[rt].cnt = 0,tr[rt].val = 0,tr[rt].cut = val;
return;
}
int mid = (l+r)>>1;
if(aim<=mid)modify(ls,l,mid,aim,flag,val);
else modify(rs,mid+1,r,aim,flag,val);
pushup(rt);
}
int main(){
#if 1
freopen("weed.in","r",stdin);
freopen("weed.out","w",stdout);
#endif
m = read(),q = read();
for(R int i = 1;i <= m;i++){
c[i] = read(),v[i] = read();
}
build(1,1,m);
for(R int i = 1;i <= q;i++){
int x = read(),a = read(),b = read();
modify(1,1,m,x,a,b);
printf("%d
",tr[1].val);
}
return 0;
}
以上是关于Weed「线段树」的主要内容,如果未能解决你的问题,请参考以下文章