线段树 延时标记数组

成段更新的重点是延迟更新,以区间[1,3]为例说明。

注意,此例中“更新”为修改元素的值为a,“查询”为求区间中所有元素之和。

建立二叉树如图示:

 

 

每一个圆圈代表一个结点,圆内数字分别为结点标号和所对应区间,[i]表示只含一个数,只出现在叶节点中。

 

当 要更新区间[1,2]中所有元素时,对应上图即要更新结点4,5。一种方法是依次访问结点4和5并更新,但这样时间和空间开销都较大,当要修改的区间的长 度较长时尤甚。于是可以采取“延迟更新”,即将更新信息储存在这段区间对应的结点处,此例中  [1,2]对应的区间是结点2,给结点一个属性tag用以 记录这个更新信息a,原来tag初始化为0,此时令tag = a。这样做实际上是将整一个区间[1,2]看做一个“点”,即应用单点更新。这样做的好处 是不需要更新结点4和5。而事实上,在这一步中我们也只需要知道整一个区间[1,2]的情况就足矣,如果以后每次查询都是查询整一个区间[1,2],又何 须分别更新结点1和2?但是,存在一些情况,如查询区间[2,3],由于上一次我们只更新了整一个区间[1,2]所对应的结点2,而没有更新结点2,此时 查询[2,3]会导致错误。此时tag起作用。要查询到结点5必须经过结点2,经过每一个结点均检查标识tag,若tag非0,表明上次没有往下执行更 新,于是执行往下更新的操作:更新1和2,然后对结点2的tag置0。

 

延迟更新的方法就是:当前父结点存储更新信息而不往下更新以减少时间可空间开销,当下次用到时再局部更新。

#include<cstdio>

#include <cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
#define lson l,m,rt<<1
#define rson r,m+1,rt<<1|1
const int maxn=55555;
int sum[maxn<<2];
int C[maxn<<2];//乘法延时标记数组 初始化为1
void down(int l,int r,int rt)
{
    if(C[rt]!=1)
    {
        sum[rt<<1]=sum[rt<<1]*C[rt];
        sum[rt<<1|1]=sum[rt<<1|1]*C[rt];
        C[rt<<1|1]=C[rt<<1]=C[rt];
        C[rt]=1;
    }
}
void Pushup(int rt)
{
    sum[rt]=sum[rt<<1]+sum[rt<<1|1]; 
}
void build(int l,int r,int rt)
{
    if(l==r)
    {
        scanf("%d",&sum[rt]);
        return;
    }
    int m=(l+r)>>1;
    build(l,m,rt<<1);
    bulid(m+1,r,rt<<1|1);
    Pushup(rt);
}
void update(int p,int add,int l,int r,int rt)//单节点跟新不需要用到延时标记数组
{
    if(l==r){
        sum[rt]=sum[rt]+add;
        return;
    }
    int m=(l+r)>>1;
    if(p<=m) update(p,add,l,m,rt<<1);
    else update(p,add,m+1,r,rt<<1|1);
    Pushup(rt);
}
int query(int L,int R,int l,int r,int rt)
//区间信息查询也不需要用到延时数组 只有修改区间的时候使用到延时数组
//但是需要使用到down数组 看这个区间的数值是否需要跟新 保证结果的正确性
{
    if(L<=l&&r<=R)
        return sum[rt];
    down(l,r,rt);
    int m=(l+r)>>1;
    int ans=0;
    if(L<=m) ans=ans+query(L,R,l,m,rt<<1);
    if(R>m)  ans=ans+query(L,R,m+1,r,rt<<1|1);
    return ans;
}
void modify(int L,int R,int l,int r,int rt,int c)
{
    if(L<=l&&r<=R)
    {
        sum[rt]=sum[rt]*c;
        C[rt]=c;//在访问之前sum的数据已经跟新了 就意味着C数组的值为0
        return;
    }
    down(l,r,rt);
    int m=(l+r)>>1;
    if(L<=m) modify( L, R,l,m,rt<<1,c);
    if(R>m)  modify( L, R,m+1,r,rt<<1|1,c);
    Pushup(rt);
}

猜你喜欢

转载自blog.csdn.net/HNUST_LIZEMING/article/details/80449231