线段树!
线段树(Segment Tree)是一种基于分治思想的二叉树结构,用于解决区间上的信息统计。与树状数组相比,线段树是一种超级多元化的数据结构。
线段树的性质:
1.线段树的每一个节点都代表一个区间。
2.线段树具有唯一的根节点,代表的区间是整个统计范围,如 [ 1 , N ] [1,N] [1,N]。
3.线段树的每个叶子节点都代表一个长度为 1 1 1的元区间 [ x , x ] [x,x] [x,x]。
4.对于每一个内部节点 [ l , r ] [l,r] [l,r],它的左子节点是 [ l , m i d ] [l,mid] [l,mid],右子节点是 [ m i d + 1 , r ] [mid+1,r] [mid+1,r]。其中 m i d = ( l + r ) / 2 mid=(l+r)/2 mid=(l+r)/2。
上图就是一棵线段树,可以看出,线段树并不一定是一棵完全二叉树,但如果把树的最后一层去掉就变成了一棵完全二叉树。所以,我们可以通过一下方式定义线段树的节点编号:
1.根节点的编号为 1 1 1。
2.标号为 x x x的节点的左儿子的编号为 x ∗ 2 x*2 x∗2,右儿子的编号为 x ∗ 2 + 1 x*2+1 x∗2+1。
这样,我们就可以定义一个结构体来保存线段树的相关信息。极端情况下,树的最大深度所处层只有一个叶子结点,其余的空余地方任然需要结构体空间。经计算,保存线段树的数组长度要不小于 4 ∗ N 4*N 4∗N才保证数组不会越界。
线段树的基本操作(以求区间最大值为例)
线段树的基本用途是对序列进行维护,支持查询与修改指令。
建树(build)
这个就轻轻松松了。直接从根节点开始,二分递归建树即可。注意在建树过程中定义节点相关信息,还要记得回溯上传有关信息。
struct node{
int l,r,mx;
}t[MAXN*4];
void build(int p,int l,int r){
t[p].l=l,t[p].r=r;
if(l==r){
t[p].mx=a[l];return;}
int mid=(l+r)/2;
build(