线段树区间更新 延时标记

来源:互联网 发布:面板数据加入虚拟变量 编辑:程序博客网 时间:2024/06/05 20:01


我在这块里先给一个B站学习线段树视频的链接,如果下面的例子看不懂的话可以去看看这个线段树的视频
http://www.bilibili.com/video/av9801549/
struct Tree{int left,right;long long lazy,sum;void update(long long x){//更新值函数,主要用在延时标记更新的时候sum +=(right-left+1)*x;lazy+=x;}}tree[N*4];int a[N];线段树空间开数组的4倍void push_down(int x){//将延时标记向下传int la = tree[x].lazy;if(la){tree[x<<1].update(la);tree[x<<1|1].update(la);tree[x].lazy = 0;}}void push_up(int id){tree[id].sum = tree[id<<1].sum+tree[id<<1|1].sum;}void Build(int id,int l,int r)//建树,主要用二分的思想
{tree[id].left = l;tree[id].right = r;tree[id].sum = tree[id].lazy = 0;if(l == r){tree[id].sum = a[l];}else{int mid = (l+r)/2;Build(id<<1,l,mid);Build(id<<1|1,mid+1,r);push_up(id);}}void update(int id,int l,int r,int val)//更新l-r值val;调用update(1,k,val);
{int L =tree[id].left,R = tree[id].right;if(l==L && R==r){tree[id].update(val);return ;}else{push_down(id);int mid = (tree[id].left+tree[id].right)/2;if(r<=mid)update(id<<1,l,r,val);else if(l>mid)update(id<<1|1,l,r,val);else{update(id<<1,l,mid,val);update(id<<1|1,mid+1,r,val);}push_up(id);}}long long query(int id,int l,int r)//更新a[k] = val;调用update(1,k,val);
{int L =tree[id].left,R = tree[id].right;if(l==L && R==r){return tree[id].sum;}
else
{push_down(id);int mid = (L+R)/2;if(r<=mid)return query(id*2,l,r);if(l>mid)return query(id*2+1,l,r);return query(id*2, l, mid)+query(id*2+1, mid + 1,r);
        }
}

我想所有学线段树区间更新的人都会被延时标记困扰,我也是看了好久才明白延时标记是什么东西,

其实可以这么想如果我们要把[3,6]的区间的值加2我们如果恰巧遇到了[3,6]这个区间,我们就把[3,6]这个区间的延时标记更新为2然后将[3,6]这个节点的sum更新;并将延时标记传给他的左右节点同时讲左右节点的值给更新,然后可以直接退出了,不需要管[3,6]左右节点的左右节点,因为我们目前用不到这些节点,延时标记就是这样,我们暂时用不到我们就把它标记上不继续向下更新,如果后面要用的时候再进行更新延时标记下面的值,比如求和[3,4]在求和函数递到[3,4]这个点的时候发现这个点的延时标记不为0那么就把这个延时标记向下传并把左右节点的值更新,这样做可以极大的节省时间,我们没必要特意的把每个点更新而是给标记上,等要求和或者跟更新其他值的时候发现这个点还有延时标记的,我们就把这个点顺便的处理一下。

0 0
原创粉丝点击