线段树区间更新【Lazy标记】【模版、详细注释】【再附上标记永久化模版】

我在前面介绍了线段树的点更新,现在讲解一下区间更新

 

  区间更新与点更新不同,它需要更深层次的理解,我讲一下最为基本的Add(L, R, V)与Query(L,R)系列,然后以此为基础可以衍射出其他各种做法:例如Set(L, R, V)系列就是其衍射品,Set系列更改的就是在查询的时候读取到Lazy标记就立刻返回,而Add系列不同,他要一直往下读它的Lazy标记。

首先,建树就不说了,跟点标记一样(因为建树又不用改变什么)。

先上一下往下更新的Pushdown操作代码:

void pushdown(int rt,int l,int r)       //非永久性标记,向下递归
{
    if(lazy[rt])
    {
        ll le = (r+l>>1)-l+1;       //左子树长度
        ll re = r-l+1-le;           //右子树长度
        lazy[rt<<1] += lazy[rt];
        lazy[rt<<1|1] += lazy[rt];
        sum[rt<<1] += lazy[rt]*le;
        sum[rt<<1|1] += lazy[rt]*re;
        lazy[rt] = 0;       //清空
    }
}

 查找:我们如果从根结点开始往下查找符合区间的点,那么就要排除那些(ql,qr)区间之外的点,但如果此时的根节点范围过大,又有Lazy标记,那么就得同时把Lazy标记往下移动了。

代码

ll query(int l,int r,int rt,int ql,int qr)      //查找函数(求和sum)
{
    if(ql<=l&&qr>=r) return sum[rt];
    else
    {
        push(rt,l,r);           //我们要去处理它的儿子节点了,就需要把Lazy标记往下推了
        int mid = l + r>>1;     //这里说明一下:‘+’优先级比'>>'符号高
        ll ret = 0;
        if(ql<=mid) ret += query(l,mid,rt<<1,ql,qr);
        if(qr>mid) ret += query(mid+1,r,rt<<1|1,ql,qr);
        return ret;
    }
}

  更新:更新与查找相似,都是从根往下,若是(ql,qr)包含了这个根所在区间,就直接给这个区间附上Lazy标记并返回,否则就说明有不必要的冗杂的东西,就得把目前这个点的Lazy标记往下移动,然后在新的区间继续找新的值。

代码

void update(int l,int r,int rt,int ql,int qr,int v)     //更新函数
{
    if(ql<=l&&qr>=r) sum[rt] += (r-l+1)*v,lazy[rt] += v;        //此处强调一定要对sum求和别忘记,不然更新父节点会少值
    else
    {
        pushdown(rt,l,r);       //说明这个根结点的范围有不在(ql, qr)范围内的,需要递归到下一层去寻找范围内的区间
        int mid = l+r>>1;
        if(ql<=mid) update(l,mid,rt<<1,ql,qr,v);
        if(qr>mid) update(mid+1,r,rt<<1|1,ql,qr,v);
        sum[rt] = sum[rt<<1] + sum[rt<<1|1];
    }
}

 

 

最后,附上永久标记话代码,我也没懂不过先做个笔记记下来,总有用到以及学会的一天。

//标记永久化(永久化标记)
void push(int rt,int l,int r)       //就是pushudown的作用
{
    if(lazy[rt]==0)return;
    ll le = (r+l>>1)-l+1;
    ll re = r-l+1-le;
    lazy[rt<<1] += lazy[rt];
    lazy[rt<<1|1] += lazy[rt];
    sum[rt<<1] += lazy[rt]*le;
    sum[rt<<1|1] += lazy[rt]*re;
    lazy[rt] = 0;
}
ll query(int l,int r,int rt,int ql,int qr)
{
    if(ql<=l&&qr>=r) return sum[rt];
    else
    {
        //push(rt,l,r);
        int mid = l + r>>1;
        ll ret = 1ll*(min(qr,r)-max(ql,l)+1) * lazy[rt];
        if(ql<=mid) ret += query(l,mid,rt<<1,ql,qr);
        if(qr>mid) ret += query(mid+1,r,rt<<1|1,ql,qr);
        return ret;
    }
}
void update(int l,int r,int rt,int ql,int qr,int v)
{
    if(ql<=l&&qr>=r) sum[rt] += 1LL*(r-l+1)*v,lazy[rt] += v;
    else
    {
        int mid = l+r>>1;
        if(ql<=mid) update(l,mid,rt<<1,ql,qr,v);
        if(qr>mid) update(mid+1,r,rt<<1|1,ql,qr,v);
        sum[rt] = sum[rt<<1] + sum[rt<<1|1] + lazy[rt]*(r-l+1);
    }
}

 

你可能感兴趣的:(数据结构,线段树,线段树区间更新)