单点更新的线段树。包括区间最值问题,区间之和问题。
线段树擅长处理一个区间之内的查询与更新。
线段树学习:
线段树上的每一个节点都维护一段区间,根节点维护的是整个区间,一个父节点的左节点维护的是将父亲的区间二等分之后的左边那个区间,右节点同理。
下面都以区间最值问题为例。可以在O(log n)的复杂度下完成查询某一个区间的最值,或者修改某一个值。
主要有三个操作 1)建树。2)查询。3)更新。
下标一般从1开始,左儿子下标为父亲的下标*2,右儿子为*2+1。
建树:
与普通的二叉树类似,如果区间就只有一个元素,那么创建这个元素,返回。递归的构建左子树,右子树,递归的同时对当前下标进行相应的操作。 总体就是一开始从根节点下来,下标为1,一直往左边,直到可以创建,然后回到父节点再往右儿子,一直递归左右。要记得的是右儿子也建立好了之后就可以更新父亲节点的值。(好像都是废话,手动模拟一下最清楚,讲不清楚)
查询:
可以分成几个情况,1)要查询区间完全包含了当前区间,这时候直接返回当前结点的值。2)如果没交集就返回不影响答案的值。3)前两种都不满足就递归左右儿子返回两者中的最值。
看了大牛的一个总结的线段树专辑,里面感觉写的很优雅。没有没有交集这种情况,而是提前判断要查询的区间与左儿子所表示的区间是否有交集,有查询下去,对于右儿子也同样检查。其实也就是将没有交集提前判断。
更新:
跟建树有一点点类似吧,也是从根节点开始,判断要更新的这个序号在左子树还是右子树,从而选择递归哪个子树更新。
最后也是要记得要更新父节点的值,才能保证整个线段树一直维护整个区间。
入门例题:
hdu 1754 单点更新 区间最值。
hdu 1166 单点更新 区间之和,所对应的每个节点维护的就是这个区间的元素和。当然也可以用树状数组做。
//hdu 1754
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define M 200009
#define INF 0x3f3f3f3
int d[M*4];
int n,m;
void pushup(int rt) 更新父节点
{
d[rt] = max(d[rt<<1],d[rt<<1|1]);
}
void bulid(int l,int r,int rt) //递归建树
{
if(l == r)
{
scanf("%d",&d[rt]);
return ;
}
int m = (l+r)>>1;
bulid(l,m,rt<<1);
bulid(m+1,r,rt<<1|1);
pushup(rt); //在右子树也建完了之后,可以由左右节点的值更新父亲节点
}
void update(int k,int a,int l,int r,int rt)
{
if(l == r)
{
d[rt] = a;
return ;
}
int m = (l+r)>>1;
if(k <= m) update(k,a,l,m,rt<<1);//要更新的点在左子树中
else update(k,a,m+1,r,rt<<1|1);
pushup(rt);
}
int query(int a,int b,int l,int r,int rt)
{
if(a <= l && b >= r) return d[rt];
int m = (l+r)>>1;
int ret = -INF;
if(a <= m) ret = max(ret,query(a,b,l,m,rt<<1));//要查询的区间与左子树的区间是否有交集
if(b > m) ret = max(ret,query(a,b,m+1,r,rt<<1|1));
return ret;
}
int main()
{
while(scanf("%d %d",&n,&m)==2)
{
bulid(1,n,1);
getchar();
for(int i = 0;i < m;i++)
{
char c = getchar();
int a,b;
scanf("%d %d",&a,&b);
getchar();
if(c == 'Q') printf("%d\n",query(a,b,1,n,1));
else update(a,b,1,n,1);
}
}
return 0;
}
//hdu 1166
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define M 50009
int n;
int d[M*4];
void pushup(int rt)
{
d[rt] = d[rt<<1] + d[rt<<1|1];
}
void build(int l,int r,int rt)
{
if(l == r)
{
scanf("%d",&d[rt]);
return ;
}
int m = (l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
pushup(rt);
}
int query(int a,int b,int l,int r,int rt)
{
if(a <= l && b >= r) return d[rt];
int m = (l+r)>>1;
int ret = 0;
if(a <= m) ret += query(a,b,l,m,rt<<1);
if(b > m) ret += query(a,b,m+1,r,rt<<1|1);
return ret;
}
void update(int k,int a,int l,int r,int rt)
{
if(l == r)
{
d[rt] += a;
return ;
}
int m = (l+r)>>1;
if(k <= m) update(k,a,l,m,rt<<1);
else update(k,a,m+1,r,rt<<1|1);
pushup(rt);
}
int main()
{
int t;
scanf("%d",&t);
int kase = 1;
while(kase <= t)
{
scanf("%d",&n);
build(1,n,1);
printf("Case %d:\n",kase);
while(1)
{
char s[20];
scanf("%s",s);
if(s[0]=='E') break;
int a,b;
scanf("%d %d",&a,&b);
if(s[0]=='Q') printf("%d\n",query(a,b,1,n,1));
else if(s[0] == 'A')
{
update(a,b,1,n,1);
}
else if(s[0] == 'S')
{
update(a,-b,1,n,1);
}
}
kase++;
}
return 0;
}