线段树进阶-pushdown

线段树进阶:\(pushdown\)

如果我们每进行一次加的操作,就将全部线段树更改一边,时间复杂度会很高。

因此,我们需要进行一个延迟加和的操作。

思路:如果 \([l,r]\) 区间增加 \(a\),在查询时,就可以把 \([l,r]\) 区间标记的增加量推下去就可以直接求值了。

这时候,我们需要记录一个懒标记lazy,来记录这个区间增加量

同时,标记分为两种:

  1. 相对标记,就是将区间所有数 \(+a\) 之类的操作,无顺序
  2. 绝对标记,类似于将区间所有数变成 \(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\) 操作,防止出错。

posted @ 2021-09-22 16:14  Evitagen  阅读(711)  评论(0编辑  收藏  举报