线段树的最基本的操作:
[1] 建树 :
void construct(int left, int right){
int index, mid;
node++;
index = node;
leftvalue[index] = left;
rightvalue[index] = right;
cover[index] = 0;
if(left + 1 < right){
mid = (left + right) / 2;
leftchild[index] = node + 1;
construct(left, mid);
rightchild[index] = node + 1;
construct(mid, right);
}
}
上述代码利用二分的方法,建立一棵叶子节点为 (i, i+ 1)的线段树 。 特点是每棵树的根,一定包含于子树的线段。
[2] 插入一段线段:
void insert(int index, int c, int d){
int mid;
if(c <= leftvalue[index] && rightvalue[index] <= d)cover[index]++;
else{
if(leftvalue[index] + 1 < rightvalue[index]){
mid = (leftvalue[index] + rightvalue[index]) / 2;
if(c < mid)insert(leftchild[index], c, d);
if(d > mid)insert(rightchild[index], c, d);
}
}
}
解释 :当要插入的线段[c, d] 覆盖了线段树上的某段线段的时候,将其标记覆盖的cover[]数组自加。如果不覆盖该线段,分为三种情况,可自己画 一下,再根据当前线段于要插入线段的关系,分情况递归左右子树
if(c <= leftvalue[index] && rightvalue[index] <= d)
cover[index]++;
c < mid && d< mid
if(c < mid)insert(leftchild[index], c, d);
c > mid && d >mid 运行
if(d > mid)insert(rightchild[index], c, d);
c < mid && d > mid 运行
if(c < mid)insert(leftchild[index], c, d);
if(d > mid)insert(rightchild[index], c, d);
[3]删除一段线段:
void Delete(int index, int c, int d){
int mid;
if(c <= leftvalue[index] && rightvalue[index] <= d && cover[index])
cover[index]--;
else{
if(leftvalue[index] + 1 < rightvalue[index]){
mid = (leftvalue[index] + rightvalue[index]) / 2;
if(c < mid)Delete(leftchild[index], c, d);
if(d > mid)Delete(rightchild[index], c, d);
}
}
}
与插入一样理解。
[4] 统计:
关于统计,有很多,也很灵活,对于每个题目来说,线段树内每个节点内的要表示的有很多,很灵活,这里只说说测度,也就是最后被覆盖的线段的总长度
void count(int index){
if(cover[index] > 0)total += y[rightvalue[index]] - y[leftvalue[index]];
else{
if(leftvalue[index] + 1 < rightvalue[index]){
count(leftchild[index]);
count(rightchild[index]);
}
}
}
理解 :既然某段线段的cover[]是正数,那么,它的子树也一定被cover,因为前面说过的“特点是每棵树的根,一定包含于子树的线段”
所以长度 = 右边界 - 左边界;
以上为线段树的最基本的操作,是对长度的操作。