线段树札记
线段树不是区间树,线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。注意他是把一段连续的区间分为单元区间为叶子节点的一颗数,以此为基础,展开一系列牛逼的计算。
首先就是如何建立这么一个线段树?
如此递归地建立,对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。就可以建立一张如下图的线段树:
伪代码:
construct(index,left,right) { tree[index].left = left; tree[index].right = right; if left=right return; mid = (left+right)/2 construct(2*index,left,mid) construct(2*index+1,mid+1,right) }
如此一个线段树就创建好了,父亲节点与孩子节点是index->(2*index,2*index+1),用此来创建一颗二叉树,所以在结构体里面只需要两个字段一个是left,一个是right
创建好线段树之后,一个重要操作就是插入操作,这个是奠定后面各种牛逼操作的基础。那么如何插入一段区间呢?(假设区间为[begin,end])
insert(index,begin,end) if begin=tree[index].left&&end=tree[index].right=end //注意这里是等于号 tree[index].cover++ int mid = (tree[index].left+tree[index].right)/2 if mid >=end insert(2*index,begin,end) else insert(2*index+1,begin,end) end end
这里为cover添加了一个字段cover,计算这段区间的区间
这个cover是后面用来查询用的
到目前为止,该用的数据结构已经维护好了。现在开始用了,查询一个点在区间中出现的次数。 从根节点开始到[x,x]叶子的这条路径中所有点计数相加方为x出现次数
查询x在区间中出现的次数。
query(index,x) mid = (tree[index].left + tree[index].right)/2 if x<=mid return query(2*index,x)+tree[index].cover else return query(2*index+1,x)+tree[index].cover end
题目练习
1,单点更新
HDU 敌兵布阵
题目很简单就是一道赤裸裸的线段树的题目。不过好久没敲代码了,生疏了,还是被一些小问题搞得很烦,可能自己内心也是太烦了。
最坑爹的是strcmp(str,"End")和str=="End"字符串的比较吧
占位符。。。。。。。。。。。。。
对于一个单节点更新的时候可以从上往下寻找的时候就更新节点的cover域,也可以在回朔的时候更新节点的cover域;
if(x>=tree[index].left&&x<=tree[index].right) // >= -> == { tree[index].cover += value; if(tree[index].left==x&&tree[index].right==x) return; }
也可以在
int mid=(tree[index].left+tree[index].right)/2; if(mid>=x) //sometime i forget to consider the equal condition,but it's important; update(2*index,x,value); else update(2*index+1,x,value); //tree[index].cover += value; 对于单节点更新,是在查找的时候更新父亲节点,还是回朔时候更新父亲节点都是可行的
代码:
#include<stdio.h> #include<string.h> #define MAX 50010 struct Node { int left; int right; int cover; }; Node * tree= new Node[MAX*4]; int data[MAX]; void build(int index,int left,int right) { tree[index].left = left; tree[index].right= right; //printf("%d %d %d ",index,left,right); if(left==right) { tree[index].cover = data[left]; // printf("%d %d %d %d ",index,left,right,tree[index].cover); return; } int mid = (left+right)/2; build(2*index,left,mid); build(2*index+1,mid+1,right); tree[index].cover = tree[2*index].cover + tree[2*index+1].cover; //update parent's cover field,not just update leaf node } void update(int index,int x,int value) { //printf("%d %d %d ",index,tree[index].left,tree[index].right); if(x>=tree[index].left&&x<=tree[index].right) // >= -> == { tree[index].cover += value; if(tree[index].left==x&&tree[index].right==x) return; } int mid=(tree[index].left+tree[index].right)/2; if(mid>=x) //sometime i forget to consider the equal condition,but it's important; update(2*index,x,value); else update(2*index+1,x,value); //tree[index].cover += value; 对于单节点更新,是在查找的时候更新父亲节点,还是回朔时候更新父亲节点都是可行的 } int query(int index,int left,int right) { //printf("%d %d %d ",index,left,right); //getchar(); if(left==tree[index].left&&right==tree[index].right) { return tree[index].cover; } int mid=(tree[index].left+tree[index].right)/2; if(mid>=right) return query(2*index,left,right); else if(mid<left) return query(2*index+1,left,right); else return query(2*index,left,mid)+query(2*index+1,mid+1,right); } int main() { int T,N; char str[20]; scanf("%d",&T); int p,v; for(int s = 1; s <= T;s++) { //printf("%d",MAX*4); memset(tree,0,sizeof(Node)*MAX*4); memset(data,0,sizeof(int)*MAX); scanf("%d",&N); for(int i = 1; i<=N;i++) { scanf("%d",&data[i]); } build(1,1,N); // for(int i = 1;i<=4*N;i++) // { // printf("%d ",tree[i].cover); // } printf("Case %d: ",s); while(true) //while(scanf("%s",str)&&str!="End") str为End时候不起作用 { scanf("%s",str); // printf("%s",str); if(strcmp(str,"End")==0) break; scanf("%d%d",&p,&v); if(strcmp(str,"Add")==0) { update(1,p,v); }else if(strcmp(str,"Sub")==0){ update(1,p,-v); } else if(strcmp(str,"Query")==0) { printf("%d ",query(1,p,v)); } } // printf("end %d",s); } return 0; }
推荐文章:
http://blog.csdn.net/wypblog/article/details/8219727
http://www.cnblogs.com/superbin/archive/2010/08/02/1790467.html
动态统计,线段树的问题。
可以更新某一个值,查询某一段区间的和
也可以更新一段区间,查询某一个元素的值
这四种操作都是线段树必须考虑的问题。