整理以前做ACM题目时候用的数据结构---线段树,说不定以后会用到。同时也当作复习。
线段树是一种高效的二叉搜索树,它比较侧重于对区间的计数操作,例如区间求和,区间最值,线段覆盖问题。以下代码为最基本的版本,只有区间求和。以后再陆续加上其它计数功能。
/*
对于线段树的每一个非叶子结点,设其表示区间为[a,b],则其左儿子表示的区间为[a,(a+b)/2],
右儿子的表示的区间为[(a+b)/2+1,b],叶结点表示的区间为[1,1],[2,2],[3,3]。。。
以下为区间[1,10]的一个例子
[1,10]
[1,5] [6,10]
[1,3] [4,5] [6,8] [9,10]
[1,2] [3,3] [4,4] [5,5] [6,7] [8,8] [9,9] [10,10]
[1,1] [2,2] [6,6][7,7]
当需要段更新时,设置一个延迟更新标记,不立即进行更新,当访问到有延迟标记的结点时,再去更新结点,然后将状态转给左右儿子
此版本为静态数组,效率比较高,但是对运行中树的大小有限制
*/
#include <iostream>
#include <stdlib.h>
template <class T>
class SegmentTree
{
static const int maxsize = 50000*5;
struct node
{
int l,r;
int cover;//区间覆盖统计
T sum;
bool updateFlag;//延迟更新标记
}s[maxsize];
int right;//最右的端点,用于避免数组访问越界
public:
void buildTree(int l,int r,int root,T v[])
{
s[root].l = l;
s[root].r = r;
//需要延迟更新,则设为true
s[root].updateFlag = false;
int mid = (l+r)>>1;
if(r>l)
{
buildTree(l,mid,root*2,v);
buildTree(mid+1,r,root*2+1,v);
s[root].sum = s[root*2].sum + s[root*2+1].sum;
}
else
{
s[root].sum = v[l];
//需要延迟更新,根据实际情况设定
s[root].updateFlag = false;
}
}
//将某个叶结点更新
void update(int leafNode,T add,int root)
{
if(s[root].updateFlag)
{
//延迟更新操作,视具体情况而定
s[root].updateFlag = false;
if(root*2 < maxsize)
{
s[root*2].updateFlag = true;
s[root*2+1].updateFlag = true;
/*
.............
.............
.............
.
.
.
.
*/
}
}
//该结点即为要更新的结点,当需要自底向上更新时
if(s[root].l == leafNode && s[root].r == leafNode)
{
s[root].sum += add;
//自底向上更新父结点
root /= 2;
while(root)
{
s[root].sum += add;
root /= 2;
}
}
else if(cur <= (s[root].l + s[root].r)/2)
update(leafNode,add,root*2);
else
update(leafNode,add,root*2+1);
}
//查询区间的和
T querry(int l,int r,int root)
{
//需要延迟标记有效,需要更新,根据实际情况修改
if(s[root].updateFlag])
{
s[root].updateFlag = false;
if(root*2 < maxsize)
{
s[root*2].updateFlag = true;
s[root*2+1].updateFlag = true;
}
/*
..
..
..
..
*/
}
if(s[root].l == l&& s[root].r == r) //该结点代表的区间恰好为所要求的区间,直接返回
return s[root].sum;
int mid = (s[root].l+s[root].r) >> 1;
if(l > mid) //所求区间全部只在该结点的右儿子
return querry(l,r,root*2+1);
else if(r <= mid) //所求区间全部只在该结点的左儿子
return querry(l,r,root*2);
else //所求区间既在左儿子又在右儿子中
return querry(l,mid,root*2) + querry(mid+1,r,root*2+1);
}
T querryInterval(int l,int r)
{
//出错,出错返回根据实际情况定义
if(l > r)
return NULL;
return querry(int l,int r,1);
}
};