线段树的定义:
首先,线段树是一棵“树”,而且是一棵完全二叉树。同时,“线段”两字反映出线段树的 另一个特点:每个节点表示的是一个“线段”,或者说是一个区间。事实上,一棵线段树的根 节点表示的是“整体”的区间,而它的左右子树也是一棵线段树,分别表示的是这个区间的左 半边和右半边。 在此我们可以举一个例子来说明线段树通常的构造方法,以RMQ问题为例: 有N个数排成一排,每次询问某一段中的最小数。 构造的时候,让根节点表示区间[0,N-1],即所有N个数所组成的一个区间,然后,把区间分 成两半,分别由左右子树表示。不难证明,这样的线段树的节点数只有2N-1个,是O(N)级 别的,如图:
对于每个节点,不但要知道它所表示的区间,以及它的儿子节点的情况,也记录一些别的值 , 不然,一棵孤零零的树能有什么用?在这个例子里,由于要查询的东西是最小值,不妨在每 个节点内记录下它所表示区间中的最小值。这样,根据一个线性表构造出线段树的方法也就 简单明白了:
线段树的建立:
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
const int maxn = 55555;
int sum[maxn<<2];
void PushUP(int rt) {
sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}
void build(int l,int r,int rt) {
if (l == r) {
scanf("%d",&sum[rt]);
return ;
}
int m = (l + r) >> 1;
build(lson);
build(rson);
PushUP(rt);
}
线段树的更新:
void update(int p,int add,int l,int r,int rt) {
if (l == r) {
sum[rt] += add;
return ;
}
int m = (l + r) >> 1;
if (p <= m) update(p , add , lson);
else update(p , add , rson);
PushUP(rt);
}
线段树的询问:
int query(int L,int R,int l,int r,int rt) {
if (L <= l && r <= R) {
return sum[rt];
}
int m = (l + r) >> 1;
int ret = 0;
if (L <= m) ret += query(L , R , lson);
if (R > m) ret += query(L , R , rson);
return ret;
}