一、建树算法

建树算法很简单也很重要,建树算法是对线段树的一个初始化操作,

如图:

因为建树的代码比较简单,所以就直接发出来了:

void build(int i, int l, int r)
{
    int mid;
    tree[i].l=l;tree[i].r=r;
    if(r==l) return;
    mid=(l+r)/2;
    build(i*2,l,mid);
    build(i*2+1,mid+1,r);
}


二、插入算法

插入算法就是一堆if语句,但是要根据题目的情况来写,每一个插入算法都不同,以下题为例:


影子的宽度

题目描述

桌子上零散地放着若干个盒子,盒子都平行于墙。桌子的后方是一堵墙。如图所示。现在从桌子的前方射来一束平行光, 把盒子的影子投射到了墙上。问影子的总宽度是多少?

输入

第1行:3个整数L,R,N。-100000 <=L<=R<= 100000,表示墙所在的区间;1<=N<=100000,表示盒子的个数
接下来N行,每行2个整数BL, BR,-100000 <=BL<=BR<= 100000,表示一个盒子的左、右端点(左闭右开)

输出

第1行:1个整数W,表示影子的总宽度。

样例输入

Sample Input 1
0 7 2
1 2
4 5

Sample Input 2
-10 10 2
-5 2
-2 2


Sample Input 3
-10 10 3
-7 0
-4 9
-4 2


Sample Input 4
-100 100 3
-7 2
5 9
2 5

Sample Input 5
-50 50 4
-2 4
0 6
9 10
-5 30

样例输出

Sample Output 1
2

Sample Output 2
7

Sample Output 3
16

Sample Output 4
16

Sample Output 5
35


这一道题求的就是投影面积的总长度,我们可以在结构体里加一个变量cover,cover表示l~r之间的区间是否被阴影完全覆盖,插入操作就是对cover变量的更新,有两种写法:
1、
void insert(int i,int l,int r)
{
int mid;
if(tree[i].cover==1) return;//当前区间已经覆盖,返回
mid=(tree[i].l+tree[i].r)/2;
if(l==tree[i].l&&r==tree[i].r)//恰好与当前区间重合
tree[i].cover=1;
else if (r <= mid)//仅在左子区间
insert(i * 2, l, r);
else if (l >= mid + 1)
insert(i * 2 + 1, l, r);//仅在右子区间
else//分在左右区间
{
insert(i * 2, l, mid);
insert(i * 2 + 1, mid + 1, r);
}
}

这一种写法分的情况比较多,而且容易写错,于是有了下面的简便写法
2、
void insert(int i,int l,int r)
{
if(r<tree[i].l||tree[i].r<l) return;
if(l<=tree[i].l&&tree[i].r<=r){
tree[i].cover=1;
return;
}
insert(i*2,l,r);
insert(i*2+1,l,r);
}
这种写法的意思是,把插入的线段区间与每一个线段树节点区间进行比较,
如果完全覆盖,cover=1,
如果没有完全覆盖,就继续递归,
如果完全没有覆盖,就退出,因为再找下去也没有意义了。

三、统计算法

这个比较简单,就直接发代码吧。

int count(int i)
{
if(tree[i].cover==1) return tree[i].r-tree[i].l+1;
else if(tree[i].l==tree[i].r ) return 0;
else return count(i*2)+count(i*2+1);
}
这个算法相当于把整个线段树进行了一次遍历,时间复杂度O(n^2),

还有一个写法是改变cover的定义,cover的新定义是l~r区间覆盖长度的总和,最后输出tree[1].cover就可以了,时间复杂度O(1)。