线段树区间更新(以POJ 3468为例)
2016-12-22 22:05
162 查看
在了解单点更新的线段树的前提下,再继续理解区间更新的线段树。
区间更新是指更新某个区间内的叶子节点的值,因为涉及到的叶子节点不止一个,而叶子节点会影响其相应的非叶父节点,那么回溯需要更新的非叶子节点也会有很多,如果一次性更新完,操作的时间复杂度肯定不是O(lgn)。为此引入了线段树中的延迟标记概念,这也是线段树的精华所在。
延迟标记:每个节点新增加一个标记,记录该节点是否进行了某种修改。将指定更新区间在树上找到对应节点后,给这些节点打上标记。在查询时,如果当前节点有标记,就给它的子节点打上该标记,并删除当前节点的标记。
延迟标记除了能够记录某个节点的是否进行修改外,通常也记录修改的情况,方便在查询时直接能够得到修改的结果。
对于区间更新的线段树,通常有四个功能:
build:左右递归建树,初始化每个节点sum值。同时将每个节点的延迟标记设置为0。
pushDown:将父节点的延迟标记传递给子节点(如果题意需要,还要将父节点的值传给子节点),同时将父节点的延迟标记清空。
query:对指定区间查询,左右子树递归查询。查询时如果父节点有延迟标记,需要向下传递。
update:对指定区间更新,左右子树递归更新。当更新到指定区间时,加上父节点的延迟标记(如果题意需要,还要进行其他操作),返回。如果没有更新到指定区间,就继续更新,如果过程中有节点有延迟标记,需要向下传递。最后回溯更新父节点的值。
注意:
update时,只有指定区间会有延迟标记,并且此时指定区间和它的父区间的sum已经更改,它的子区间和其他区间没有延迟标记和修改值。
query时,延迟标记会传至子区间,并且同时修改子区间的值,直到找到指定区间。此时,延迟标记在指定区间上,指定区间的子区间的sum值没有修改。
对于POJ 3468:
在pushDown和update时,子区间需要加上父区间传过来的值*区间元素数;
(具体操作见代码)
区间更新是指更新某个区间内的叶子节点的值,因为涉及到的叶子节点不止一个,而叶子节点会影响其相应的非叶父节点,那么回溯需要更新的非叶子节点也会有很多,如果一次性更新完,操作的时间复杂度肯定不是O(lgn)。为此引入了线段树中的延迟标记概念,这也是线段树的精华所在。
延迟标记:每个节点新增加一个标记,记录该节点是否进行了某种修改。将指定更新区间在树上找到对应节点后,给这些节点打上标记。在查询时,如果当前节点有标记,就给它的子节点打上该标记,并删除当前节点的标记。
延迟标记除了能够记录某个节点的是否进行修改外,通常也记录修改的情况,方便在查询时直接能够得到修改的结果。
struct Node{ ll l,r; ll sum,add; }
对于区间更新的线段树,通常有四个功能:
build:左右递归建树,初始化每个节点sum值。同时将每个节点的延迟标记设置为0。
pushDown:将父节点的延迟标记传递给子节点(如果题意需要,还要将父节点的值传给子节点),同时将父节点的延迟标记清空。
query:对指定区间查询,左右子树递归查询。查询时如果父节点有延迟标记,需要向下传递。
update:对指定区间更新,左右子树递归更新。当更新到指定区间时,加上父节点的延迟标记(如果题意需要,还要进行其他操作),返回。如果没有更新到指定区间,就继续更新,如果过程中有节点有延迟标记,需要向下传递。最后回溯更新父节点的值。
注意:
update时,只有指定区间会有延迟标记,并且此时指定区间和它的父区间的sum已经更改,它的子区间和其他区间没有延迟标记和修改值。
query时,延迟标记会传至子区间,并且同时修改子区间的值,直到找到指定区间。此时,延迟标记在指定区间上,指定区间的子区间的sum值没有修改。
对于POJ 3468:
在pushDown和update时,子区间需要加上父区间传过来的值*区间元素数;
(具体操作见代码)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
const int N=1e5+10;
ll a
;
char opr[10];
struct Node{ ll l,r; ll sum,add; }tree[4*N];
void build(ll i,ll l,ll r){
tree[i].l=l,tree[i].r=r;
tree[i].add=0;
if(l==r){//左边=右边,单点建立
tree[i].sum=a[l];
return;
}
ll tmp=i<<1;
ll mid=(l+r)>>1;
build(tmp,l,mid);//左子树
build(tmp+1,mid+1,r);//右子树
tree[i].sum=tree[tmp].sum+tree[tmp+1].sum;//回溯得到区间sum值
}
void pushDown(ll x){
ll tmp=x<<1;
tree[tmp].add+=tree[x].add;//左子树得到父节点标记
tree[tmp+1].add+=tree[x].add;//右子树得到父节点标记
//如果区间内每个元素加上一个值,那么这个区间会加上当前区间包含指定区间元素个数*待加元素的值
tree[tmp].sum+=tree[x].add*(tree[tmp].r-tree[tmp].l+1);
tree[tmp+1].sum+=tree[x].add*(tree[tmp+1].r-tree[tmp+1].l+1);
tree[x].add=0;//清空父节点标记
}
ll query(ll i,ll l,ll r){
if(tree[i].l>r||tree[i].r<l){//超出范围
return 0;
}
if(tree[i].l>=l&&tree[i].r<=r){//刚好在落到求得的区间
return tree[i].sum;
}
//如果查询时发现当前区间不为目标区间,但是有延迟标记时,将延迟标记传递
if(tree[i].add) pushDown(i);
ll tmp=i<<1;
//左右子树分别查询,将查询结果相加
//如果某一子树超出范围,会在第一个条件处return 0
return query(tmp,l,r)+query(tmp+1,l,r);
}
void update(ll i,ll l,ll r,ll s){
if(tree[i].l>r||tree[i].r<l){//超出范围
return ;
}
//在指定区间的范围内,该点加上sum与延迟标记.此时包含该点的父区间均更新完毕
//>=、<=是考虑到区间位于不同子树的情况
if(tree[i].l>=l&&tree[i].r<=r){
tree[i].add+=s;
tree[i].sum+=s*(tree[i].r-tree[i].l+1);
return;
}
//如果之前已经有延迟标记,需要将之前的标记处理
if(tree[i].add) pushDown(i);
ll tmp=i<<1;
//更新左右子树,如果某个子树超出了所更新区间的范围,会在第一个条件处return
update(tmp,l,r,s);
update(tmp+1,l,r,s);
tree[i].sum=tree[tmp].sum+tree[tmp+1].sum;//回溯更新父节点的值
}
int main(){
int n,q;
while(~scanf("%d %d",&n,&q)){
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
build(1,1,n);
ll l,r,s;
while(q--){
scanf("%s",opr);
if(opr[0]=='Q'){
scanf("%lld %lld",&l,&r);
cout<<query(1,l,r)<<endl;
}
else if(opr[0]=='C'){
scanf("%lld %lld %lld",&l,&r,&s);
update(1,l,r,s);
}
}
}
}
/*
10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4
5 10
1 2 3 4 5
C 3 5 2
Q 3 5
*/
相关文章推荐
- POJ 3468 A Simple Problem with Integers(线段树区间更新)
- poj-3468 线段树和树状数组的区间更新及求和
- poj 3468 线段树区间更新维护
- poj 3468 (线段树区间更新及求和)
- poj - 3468 - A Simple Problem with Integers(线段树(区间更新))
- poj 3468 A Simple Problem with Integers 线段树 区间更新求和
- 20140719 「线段树 - 区间更新,区间求和」 POJ 3468 A Simple Problem with Integers
- 线段树 + 区间更新 + 模板 ---- poj 3468
- POJ 3468 A Simple Problem with Integers(线段树 区间更新)
- POJ 3468 A Simple Problem with Integers(线段树区间更新+求和)
- POJ-3468(线段树_区间更新)
- POJ-3468-A Simple Problem with Integers(线段树区间更新)
- POJ 3468 (线段树,区间更新,查询区间)
- 线段树 : 区间更新 poj 3468 示例
- poj 3468 A Simple Problem with Integers(线段树区间更新 or 树状数组区间更新)
- [poj 3468]A Simple Problem with Interger[线段树][区间更新]
- POJ 3468 A Simple Problem with Integers(线段树区间更新,模板题,求区间和)
- POJ-3468-A Simple Problem with Integers(区间更新,求和)-splay或线段树
- 线段树 : 区间更新 poj 3468 示例
- POJ 3468 A Simple Problem with Integers 【线段树,区间更新】