我在这块里先给一个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那么就把这个延时标记向下传并把左右节点的值更新,这样做可以极大的节省时间,我们没必要特意的把每个点更新而是给标记上,等要求和或者跟更新其他值的时候发现这个点还有延时标记的,我们就把这个点顺便的处理一下。