背景:记录区间值,便于查询
常见应用:维护区间和,区间最大值
常见操作:点值更新,区间更新,区间查询
tip:lazy标记,先更新父节点的标记情况,等到有需要的时候再去更新孩子的标记情况
值得注意的地方:两个函数:push_up(int root)自底向下更新父亲节点;push_down(int root,int len)自父亲向下更新lazy标记
在递归下降过程中,当我们更新了叶子节点的信息以后,一定要记得push_up一下更新父亲节点信息
需要的空间 int a[MaxNum<<2]
板子:
#include <bits/stdc++.h>
using namespace std;
//l,r:区间左右端点 root:当前结点在内存池中的位置
void build(int l,int r,int root)
{
if(l==r)
{
num[root]=1;
return;
}
//[l,mid]左子树, [mid+1,r]:右子树
int mid=(l+r)>>1;
build(l,mid,root<<1);
build(mid+1,r,root<<1|1);
push_up(root);
//先将左右子树都算出来,再来更新它的值
//沿路回溯,回溯到点root,都是被[la,lb]或其子区间影响到到的点,边回溯边更新
} //调用build[1,N,1]
//根结点状态更新
void push_up(int root)
{
num[root]=num[root<<1]+num[root<<1|1];
}
//线段树点的修改(单点更新)
void upgrade(int p,int val,int l,int r,int root)
{
if(l==r)
{
num[root]+=val;
return;
}
int mid=(l+r)>>1;
if(p>mid)
upgrade(p,val,mid+1,r,root<<1|1);
else
upgrade(p,val,mid,root<<1);
//沿路回溯
//num[root]=num[root<<1]+num[root<<1|1];
push_up(root);
}
//线段树的区间修改
//打上lazy标记的点自己本身已经更新了
//[la,lb]为需要更性的区间(一直不变)
//[l,r]为当前区间的左右端点(随递归更新)
//root为当前[l,r]对映的根存储位置(随递归更新)
void upgrade_2(int la,int rb,int l,int r,int val,int root)
{
if(la<=l&&rb>=r)
{
num[root]=(r-l+1)*val;
lazy[root]=val;
return;
}
push_down(root,r-l+1);
//在继续递归之前先把root标记往下推
int mid=(l+r)>>1;
if(la<=mid)
upgrade_2(la,rb,l,mid,val,root<<1);
if(rb>mid)
upgrade_2(la,rb,mid+1,r,val,root<<1|1);
push_up(root);
}
//push_up函数:假设两个子节点的信息完全知道,推出父节点的信息
//push_down函数:与lazy的使用有关
//传入父节点root和区间长度len
void push_down(int root,int len)
{
if(lazy[root]==0)
return;
lazy[root<<1]=lazy[root];
lazy[root<<1|1]=lazy[root];
num[root<<1]=lazy[root<<1]*(len-(len)/2);
num[root<<1|1]=lazy[root<<1|1]*len/2;
lazy[root]=0;
}
//查询区间[la,rb]的值
int query(int la,int rb,int l,int r,int root)
{
if(l>=la&&r<=rb)
return num[root];
push_down(root,r-l+1);
int mid=(l+r)<<1;
int ans=0;
if(la<=mid)
ans+=query(la,rb,l,mid,root<<1);
if(rb>mid)
ans+=query(la,rb,mid+1,r,root<<1|1);
return ans;
}