思路:
线段树,每个节点都被模拟成一条线段,负责维护这条线段(下面成为区间)从从左端点到有端点这一部分的信息。
大致思路是这样的:一个父节点负责维护一块大区间的信息,他有两个左右子节点,编号为分别负责维护父节点的左一半区间和右一半区间。
这个图是维护区间最大值的线段树.
数据结构:
根节点编号为1。编号为rt的节点,维护区间是[l,r];左儿子编号为rt*2,维护[l,(r+l)/2];右儿子编号为rt*2+1,维护[(r+l)/2+1,r]。
线段树是一颗完全二叉树,维护[1,n]序列的线段树右2n-1个节点,所以线段树要开四倍空间。
模板:
建树
int a[N],sum[N*4]; void init(){ memset(a,0,sizeof(a)); memset(sum,0,sizeof(sum)); } void push_up(int rt){ sum[rt]=sum[rt<<1]+sum[rt<<1|1]; //父区间点的子区间之和 } void bulid(int rt,int l,int r){ //从根节点的编号,1开始,自上而下建树 if(l==r){ //递归边界,达到叶节点 sum[rt]=a[l]; return ; } int mid=(l+r)>>1; bulid(rt<<1,l,mid); //左 bulid(rt<<1|1,mid+1,r); //右 push_up(rt); //回溯:子节点建成,组成父节点 }
单点更新(增值),查询区间和
1 void update(int rt,int l,int r,int p,int v){ 2 if(l==r){ //到达修改区间的叶节点 3 sum[rt]+=v; 4 return ; 5 } 6 int mid=(l+r)>>1; 7 if(p<=mid) //向左更新 8 update(rt<<1,l,mid,p,v); 9 else if(p>mid) //向右更新 10 update(rt<<1|1,mid+1,r,p,v); 11 push_up(rt); //子节点更新完成,更新父节点 12 } 13 int query(int rt,int l,int r,int ll,int rr){ 14 if(ll<=l&&r<=rr){ //查询区间完全包含当前区间 15 return sum[rt]; 16 } 17 int mid=(l+r)>>1; 18 int res=0; 19 if(ll<=mid) res+=query(rt<<1,l,mid,ll,rr); //访问查询区间在当前左区间的部分 20 if(rr>mid) res+=query(rt<<1|1,mid+1,r,ll,rr); 21 return res; 22 }
单点改值
即第3行改成 : sum[rt]=v; query()中的v ,代表a[p]=v;
单点增值 求区间最大数
1 const int N=5e4+10; 2 int a[N],MAX[N*4]; 3 void init(){ 4 memset(a,0,sizeof(a)); 5 memset(MAX,0,sizeof(MAX)); 6 } 7 void push_up(int rt){ 8 MAX[rt]=max(MAX[rt<<1],MAX[rt<<1|1]); 9 } 10 void bulid(int rt,int l,int r){ 11 if(l==r){ 12 MAX[rt]=a[l]; 13 return ; 14 } 15 int mid=(l+r)>>1; 16 bulid(rt<<1,l,mid); 17 bulid(rt<<1|1,mid+1,r); 18 push_up(rt); 19 } 20 void update(int rt,int l,int r,int p,int v){ 21 if(l==r){ 22 MAX[rt]+=v; 23 return ; 24 } 25 int mid=(l+r)>>1; 26 if(p<=mid) 27 update(rt<<1,l,mid,p,v); 28 else if(p>mid) 29 update(rt<<1|1,mid+1,r,p,v); 30 push_up(rt); 31 } 32 int query(int rt,int l,int r,int ll,int rr){ 33 if(ll<=l&&r<=rr){ 34 return MAX[rt]; 35 } 36 int mid=(l+r)>>1; 37 int res=0; 38 if(ll<=mid) res=max(res,query(rt<<1,l,mid,ll,rr)); 39 if(rr>mid) res=max(res,query(rt<<1|1,mid+1,r,ll,rr)); 40 return res; 41 }
区间改值 :第16行 改为MAX[rt]=v;