线段树进阶-pushdown
线段树进阶:\(pushdown\)
如果我们每进行一次加的操作,就将全部线段树更改一边,时间复杂度会很高。
因此,我们需要进行一个延迟加和的操作。
思路:如果 \([l,r]\) 区间增加 \(a\),在查询时,就可以把 \([l,r]\) 区间标记的增加量推下去就可以直接求值了。
这时候,我们需要记录一个懒标记lazy,来记录这个区间增加量
同时,标记分为两种:
- 相对标记,就是将区间所有数 \(+a\) 之类的操作,无顺序
- 绝对标记,类似于将区间所有数变成 \(a\) ,此时必须在区间修改时下推旧标记
实现过程:
因为懒标记的出现,我们对于线段树的一些基本操作就需要对应的改变。
对于树的建立,我们有:
struct tree{
int l,r,lazy,sum;
}t[N];
pushup操作
void pushup(int x){
t[x].sum=t[x<<1].sum+t[x<<1|1].sum;
}
单点修改(直接改就行):
void add(int x,int k,int dis){
if(t[x].l==t[x].r){
t[x].sum+=k;
return;
}
if(dis<=t[x<<1].r) add(x<<1,k,dis);
else add(x<<1|1,k,dis);
pushup(x)//回溯时需要重新赋值
}
区间修改
void update(int l,int r,int c,int x){
if(t[x].l>=l&&t[x].r<=r){
t[x].lazy+=c;
t[x].sum+=c;
return;
}
pushdown(x);
int mid=t[x].l+t[x].r>>1;
if(mid>=l) update(l,r,c,x<<1);
if(mid<r) update(l,r,c,x<<1|1);
pushup(x);
}
区间查询
int query(int l,int r,int x){
if(l<=t[x].l&&r>=t[x].r) return t[x].sum;
pushdown(x);
int mid=t[x].l+t[x].r>>1;
if(mid>=l) query(l,r,x<<1);
if(mid<r) query(l,r,x<<1|1);
return res;
}
关键部分:\(pushdown\)
void pushdown(int x){
if(t[x].lazy){
int lz=t[x].lazy;
t[x<<1].lazy+=lz;//增加懒标记
t[x<<1|1].lazy+=lz;
t[x<<1].sum+=t[x].lz*len(x<<1);//值*区间长度
t[x<<1|1].sum+=t[x].lz*len(x<<1|1);
t[x].lazy=0;
}
}
我们还需要记住,当出现乘法操作与加减法操作混合时,一定要在乘法出现时对其进行 \(pushdown\) 操作,防止出错。
不关注的有难了😠😠😠https://b23.tv/hoXKV9