区间更新是指更新某个区间内的叶子节点的值,因为涉及到的叶子节点不止一个,而叶子节点会影响其相应的非叶父节点,那么回溯需要更新的非叶子节点也会有很多,如果一次性更新完,操作的时间复杂度肯定不是O(lgn),例如当我们要更新区间[0,3]内的叶子节点时,需要更新出了叶子节点3,9外的所有其他节点。为此引入了线段树中的延迟标记概念,这也是线段树的精华所在。
延迟标记:每个节点新增加一个标记,记录这个节点是否进行了某种修改(这种修改操作会影响其子节点),对于任意区间的修改,我们先按照区间查询的方式将其划分成线段树中的节点,然后修改这些节点的信息,并给这些节点标记上代表这种修改操作的标记。在修改和查询的时候,如果我们到了一个节点p,并且决定考虑其子节点,那么我们就要看节点p是否被标记,如果有,就要按照标记修改其子节点的信息,并且给子节点都标上相同的标记,同时消掉节点p的标记。
因此需要在线段树结构中加入延迟标记域,本文例子中我们加入标记与addMark,表示节点的子孙节点在原来的值的基础上加上addMark的值,同时还需要修改创建函数build 和 查询函数 query,修改的代码用红色字体表示,其中区间更新的函数为update,代码如下:
const int INFINITE = INT_MAX;
const int MAXNUM = 1000;
struct SegTreeNode
{
int val;
int addMark;//延迟标记
}segTree[MAXNUM];//定义线段树
/*
功能:构建线段树
root:当前线段树的根节点下标
arr: 用来构造线段树的数组
istart:数组的起始位置
iend:数组的结束位置
*/
void build(int root, int arr[], int istart, int iend)
{
segTree[root].addMark = 0;//----设置标延迟记域
if(istart == iend)//叶子节点
segTree[root].val = arr[istart];
else
{
int mid = (istart + iend) / 2;
build(root*2+1, arr, istart, mid);//递归构造左子树
build(root*2+2, arr, mid+1, iend);//递归构造右子树
//根据左右子树根节点的值,更新当前根节点的值
segTree[root].val = min(segTree[root*2+1].val, segTree[root*2+2].val);
}
}
/*
功能:当前节点的标志域向孩子节点传递
root: 当前线段树的根节点下标
*/
void pushDown(int root)
{
if(segTree[root].addMark != 0)
{
//设置左右孩子节点的标志域,因为孩子节点可能被多次延迟标记又没有向下传递
//所以是 “+=”
segTree[root*2+1].addMark += segTree[root].addMark;
segTree[root*2+2].addMark += segTree[root].addMark;
//根据标志域设置孩子节点的值。因为我们是求区间最小值,因此当区间内每个元
//素加上一个值时,区间的最小值也加上这个值
segTree[root*2+1].val += segTree[root].addMark;
segTree[root*2+2].val += segTree[root].addMark;
//传递后,当前节点标记域清空
segTree[root].addMark = 0;
}
}
/*
功能:线段树的区间查询
root:当前线段树的根节点下标
[nstart, nend]: 当前节点所表示的区间
[qstart, qend]: 此次查询的区间
*/
int query(int root, int nstart, int nend, int qstart, int qend)
{
//查询区间和当前节点区间没有交集
if(qstart > nend || qend < nstart)
return INFINITE;
//当前节点区间包含在查询区间内
if(qstart <= nstart && qend >= nend)
return segTree[root].val;
//分别从左右子树查询,返回两者查询结果的较小值
pushDown(root); //----延迟标志域向下传递
int mid = (nstart + nend) / 2;
return min(query(root*2+1, nstart, mid, qstart, qend),
query(root*2+2, mid + 1, nend, qstart, qend));
}
/*
功能:更新线段树中某个区间内叶子节点的值
root:当前线段树的根节点下标
[nstart, nend]: 当前节点所表示的区间
[ustart, uend]: 待更新的区间
addVal: 更新的值(原来的值加上addVal)
*/
void update(int root, int nstart, int nend, int ustart, int uend, int addVal)
{
//更新区间和当前节点区间没有交集
if(ustart > nend || uend < nstart)
return ;
//当前节点区间包含在更新区间内
if(ustart <= nstart && uend >= nend)
{
segTree[root].addMark += addVal;
segTree[root].val += addVal;
return ;
}
pushDown(root); //延迟标记向下传递
//更新左右孩子节点
int mid = (nstart + nend) / 2;
update(root*2+1, nstart, mid, ustart, uend, addVal);
update(root*2+2, mid+1, nend, ustart, uend, addVal);
//根据左右子树的值回溯更新当前节点的值
segTree[root].val = min(segTree[root*2+1].val, segTree[root*2+2].val);
}
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn=1000000+5;
const int inf=0x3f3f3f3f;
int n,m;
struct node
{
long long val;
long long add;
node(long long vall=0,long long addd=0)
{
val=vall;add=addd;
}
};
node tree[maxn*2];
long long arr[maxn];
int T;
void build(int root,int istart,int iend)
{
tree[root].add=0;
if(istart==iend)
tree[root].val=arr[istart];
else
{
int mid=(istart+iend)/2;
build(root*2,istart,mid);
build(root*2+1,mid+1,iend);
tree[root].val=tree[root*2].val+tree[root*2+1].val;
//cout<iend)
return 0;
if(l<=istart&&r>=iend)
{
// cout<mid) ans+=que(root*2+1,mid+1,iend,l,r);
return ans;
}
void update(int root,int istart,int iend,int l,int r,int a)
{
if(l>iend||r=iend)
{
tree[root].add+=a;
tree[root].val+=(iend-istart+1)*a; //和值需要乘积
}
else
{
pushdown(root,istart,iend);
int mid=(istart+iend)/2;
update(root*2,istart,mid,l,r,a);
update(root*2+1,mid+1,iend,l,r,a);
tree[root].val=tree[root*2].val+tree[root*2+1].val;
}
}
int main()
{
cin>>n>>m;
memset(tree,0,sizeof(0));
for(int i=1;i<=n;i++)
scanf("%lld",&arr[i]);
build(1,1,n);
for(int i=1;i<=m;i++)
{
char ch;
int a,b,c;
cin>>ch>>a>>b;
if(ch=='Q')
// cout<>c;
update(1,1,n,a,b,c);
}
}
return 0;
}
HDU - 1698
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn=200000+5;
const int inf=0x3f3f3f3f;
int n,m;
struct node
{
int val;
int add;
};
node tree[maxn*2];
int T;
void build(int root,int istart,int iend)
{
tree[root].add=0;
if(istart==iend)
tree[root].val=1;
else
{
int mid=(istart+iend)/2;
build(root*2,istart,mid);
build(root*2+1,mid+1,iend);
tree[root].val=max(tree[root*2].val,tree[root*2+1].val);
}
}
void pushdown(int root,int l,int r)
{
if(tree[root].add)
{
int mid=(l+r)/2;
tree[root*2].add=tree[root].add;
tree[root*2].val=tree[root].add*(mid-l+1);
tree[root*2+1].add=tree[root].add;
tree[root*2+1].val=tree[root].add*(r-mid);
tree[root].add=0;
}
}
void update(int root,int istart,int iend,int l,int r,int a)
{
if(l>iend||r=iend)
{
tree[root].add=a;
tree[root].val=(iend-istart+1)*a;
}
else
{
pushdown(root,istart,iend);
int mid=(istart+iend)/2;
update(root*2,istart,mid,l,r,a);
update(root*2+1,mid+1,iend,l,r,a);
tree[root].val=tree[root*2].val+tree[root*2+1].val;
}
}
int main()
{
int T;
cin>>T;
for(int ka=1;ka<=T;ka++)
{ cin>>n;
build(1,1,n);
cin>>m;
for(int i=1;i<=m;i++)
{
int x,y,z;
cin>>x>>y>>z;
update(1,1,n,x,y,z);
}
printf("Case %d: The total value of the hook is %d.\n",ka,tree[1].val);
}
return 0;
}