线段树
线段树札记
线段树不是区间树,线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。注意他是把一段连续的区间分为单元区间为叶子节点的一颗数,以此为基础,展开一系列牛逼的计算。
首先就是如何建立这么一个线段树?
如此递归地建立,对于线段树中的每一个非叶子节点[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\n",index,left,right);
if(left==right)
{
tree[index].cover = data[left];
// printf("%d %d %d %d\n",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\n",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\n",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:\n",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\n",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
动态统计,线段树的问题。
可以更新某一个值,查询某一段区间的和
也可以更新一段区间,查询某一个元素的值
这四种操作都是线段树必须考虑的问题。
posted on 2014-05-11 15:28 Champion Lai 阅读(241) 评论(0) 收藏 举报
浙公网安备 33010602011771号