树结构的基本思想是分割。普通二叉搜索树是按照对象来进行划分,因此效果往往和数据结构内对象有关;而线段树是根据关键码的可能范围来分的,这种技术叫做关键空间分解。
线段树的处理对象是线段(一般意义上的区间也可以看成是线段),它把线段组织成利于检索和统计的形式,它的本质是线段的二叉搜索树。但是线段可以分解和合并,线段树又有一些一般二叉树没有的特殊的操作。另外,线段树操作的是整个区间,它的渐进时间效率不依赖于数据结构中的对象。
线段树的定义:线段树是一颗二叉搜索树,最终将一个区间[1,n]划分为一些[i, i+1]的单元区间,每个单元区间对应线段树中的一个节点。每个节点用变量count记录覆盖该节点的线段条数。
线段树处理的对象:线段树的处理对象时一段狭窄的区间,区间上的格点对应有限个固定的变量,好像是线性的数组用完全二叉排序树形式表达。处理问题的时候,抽象出区间上的格点,也即是明确每个格点对应变量的含义。
线段树的实现和空间耗费:如果节点i的处理对象时区间[a,b],那么区间[a,(a+b)/2]和[(a+b)/2,b]分别是i左儿子和右儿子的处理对象。当求解区间[a,b]内某个参数的统计值时,可以直接调用i上的统计信息,而无需将区间[a,a]->[b,b]的信息一个个累加。这是线段树在时间效率上主要的优点。和堆及二叉树相同,线段树也是动态维护的数据结构。空间复杂度最坏情况下是满二叉树。
线段树的插入和删除:显然,算法是基于二分的。和一般的BST不一样的是,只要考虑的总区间不变,线段树的节点本身是不变的。改变的知识计数器Ci和其他信息。
线段树的扩充:除了完全覆盖区间的线段条数Ci之外,往往还需要记录两个量来帮助我们获得更多的信息:
测度m:节点所表示区间中线段覆盖过的长度
独立线段树line:指的是区间中互不相交的线段条数
权和sum:区间所有元线段的权和。
HDU(1166):
#include <iostream>
#include <stdio.h>
#include <cstdlib>
#include <string.h>
#include <algorithm>
using namespace std;
#define N 50005
struct SegmentTree {
int l, r, mid;
int sum;
}ST[4*N];
int data[N];
inline void init(int l, int r, int n) {
ST[n].l = l;
ST[n].r = r;
ST[n].mid = (l+r)>>1;
ST[n].sum = 0;
if (l+1 == r)
return;
init(l, (l+r)>>1, 2*n);
init((l+r)>>1, r, 2*n+1);
};
inline void add(int pos, int value, int n) {
ST[n].sum += value;
if (ST[n].l + 1 == ST[n].r)
return;
if (pos < ST[n].mid) {
add(pos, value, 2*n);
} else {
add(pos, value, 2*n+1);
}
};
inline void sub(int pos, int value, int n) {
ST[n].sum -= value;
if (ST[n].l + 1 == ST[n].r)
return;
if (pos < ST[n].mid) {
sub(pos, value, 2*n);
} else {
sub(pos, value, 2*n+1);
}
};
inline int query(int l, int r, int n) {
if (ST[n].l == l && ST[n].r == r)
return ST[n].sum;
if (l < ST[n].mid) {
if (r <= ST[n].mid) {
return query(l, r, 2*n);
} else {
return query(l, ST[n].mid, 2*n) + query(ST[n].mid, r, 2*n+1);
}
} else {
return query(l, r, 2*n+1);
}
};
int main()
{
//freopen("C://Users//smile//Desktop//in.txt","r",stdin);
//freopen("C://Users//smile//Desktop//out.txt","w",stdout);
int T, cas = 1;
scanf("%d",&T);
while (T --) {
int n, tmp;
char in[10];
scanf("%d",&n);
init(1, n+1, 1);
for (int i = 1; i <= n; ++ i) {
scanf("%d",&tmp);
add(i, tmp, 1);
}
printf("Case %d:\n",cas++);
while (scanf("%s",in), strcmp(in,"End")) {
int l, r;
scanf("%d%d",&l,&r);
if (in[0] == 'Q') {
printf("%d\n", query(l, r+1, 1));
} else if (in[0] == 'A') {
add(l, r, 1);
} else {
sub(l, r, 1);
}
}
}
//fclose(stdin);
//fclose(stdout);
}