思路:
线段树,每个节点都被模拟成一条线段,负责维护这条线段(下面成为区间)从从左端点到有端点这一部分的信息。
大致思路是这样的:一个父节点负责维护一块大区间的信息,他有两个左右子节点,编号为分别负责维护父节点的左一半区间和右一半区间。
这个图是维护区间最大值的线段树.
数据结构:
根节点编号为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;