线段树的定义
经常遇到一些与区间有关的题目,这类题目用通常的方法很麻烦,但是有一种特殊的数据结构——线段树(Segment Tree)可以很好地解决有关区间上的问题。
线段树是一棵二叉树,记为T(a,b),参数a,b表示区间[a,b],其中,b-a为区间的长度,记为L。
其根为:[a,b]
其左儿子为:[a,(a+b)/2]
其右儿子为:[(a+b)/2,b]
若L=1,则为叶子节点。
//
结点类型
typedef struct node
... {
int l, r;
int count;
//lbd:
//左端点被覆盖为true,未被覆盖为false
//rbd相同
bool lbd, rbd;
node *left, *right;
} * nodeptr;
// 线段树
// 支持的操作有:
// insert(a, b) 插入一条线段[a,b]
// remove(a, b) 当[a, b]这条线段存在时,删除
// count(a, b) 统计[a, b]被覆盖的次数
// count(a, b)总是返回[a, b]中最少的覆盖次数,如
// 如果[a, b]仅被覆盖过一次,当[p, q]∈[a, b]
// 且[p, q]被覆盖,[a, p],[q, b]没有被覆盖时,
// count(a, b)只返回1
// measure(a, b) 返回[a, b]间的测度(测度m指的是区间中线段覆盖过的长度)
// lines(a, b) 返回[a, b]间连续线段的数量
// 几个例子:
// insert(3, 6), insert(4, 9), insert(11, 12)之后
// count(3, 6) = 1, count(4, 6) = 2, count(1, 4) = 0
// measure(1, 4) = 1, measure(3, 9) = 6, measure(5, 10) = 4
// lines(1, 9) = 1, line(5, 12) = 2
class Segment_Tree
... {
private:
nodeptr root;
//递归建树
void create(nodeptr& current, int a, int b)
...{
//初始化
current = new node;
current -> l = a; current -> r = b; current -> count = current -> lbd = current -> rbd = 0;
if (b - a == 1) //到达叶子,结束
current -> left = current -> right = NULL;
else //递归调用
...{
nodeptr lc, rc;
create(lc, a, (a+b) / 2);
create(rc, (a+b) / 2, b);
current -> left = lc;
current -> right = rc;
}
}
//插入一条线段
void insert(nodeptr p, int a, int b)
...{
//用于计算lines
p -> lbd |= (p -> l == a);
p -> rbd |= (p -> r == b);
//刚好是这一段,直接更新
if (p -> l == a && p -> r == b)
p -> count++;
else
...{
int m = (p -> l + p -> r) / 2; //a, b的中点
if (b <= m)
insert(p -> left, a, b); //需要在[a, m]插入[a, b]
else if (a >= m)
insert(p -> right, a, b); //在[m, b]中插入[a, b]
else //二分插入
...{
insert(p -> left, a, m);
insert(p -> right, m, b);
}
}
}
//删除一条线段
void remove(nodeptr p, int a, int b)
...{
//用于计算lines
p -> lbd &= !(p -> l == a);
p -> rbd &= !(p -> r == b);
//刚好是这一段,直接更新
if (p -> l == a && p -> r == b)
p -> count--;
else
...{
int m = (p -> l + p -> r) / 2; //a, b的中点
if (b <= m)
remove(p -> left, a, b); //需要在[a, m]删除[a, b]
else if (a >= m)
remove(p -> right, a, b); //在[m, b]中删除[a, b]
else //二分删除
...{
remove(p -> left, a, m);
remove(p -> right, m, b);
}
}
}
//统计[a,b]被覆盖的次数
//有待研究
int count(nodeptr p, int a, int b)
...{
if (p -> r - p -> l == 1)
return p -> count;
else
...{
int m = (p -> l + p -> r) / 2;
if (b <= m)
return count(p -> left, a, b) + p -> count;
else if (a >= m)
return count(p -> right, a, b) + p -> count;
else
return min(count(p -> left, a, m), count(p -> right, m, b)) + p -> count;
}
}
//计算[a,b]的测度
//结点的测度m指的是结点所表示区间中线段覆盖过的长度
//m = b - a (count > 0)
//m = 0 (count = 0且为叶结点)
//m = left.m + right.m (count = 0且为内部结点)
int measure(nodeptr p, int a, int b)
...{
if (count(a, b) > 0)
return (b - a);
if (b - a == 1)
return 0;
int m = (p -> l + p -> r) / 2;
return measure(p -> left, a, m) + measure(p -> right, m, b);
}
//计算[a,b]的连续线段数
//连续线段数line指的是区间中互不相交的线段条数
//计算方法:
//line = 1 (count > 0)
//line = 0 (count = 0且为叶结点)
//line = left.line + right.line - 1 (count = 0且为内部结点,且left.rbd和right.lbd同时为1)
//line = left.line + right.line (count = 0且为内部结点,且left.rbd和right.lbd不同时为1)
int lines(nodeptr p, int a, int b)
...{
if (count(a, b) > 0)
return 1;
if (b - a == 1)
return 0;
int m = (p -> l + p -> r) / 2;
return lines(p -> left, a, m) + lines(p -> right, m, b) - (p -> left -> rbd & p -> right -> lbd);
}
public:
//初始化时,指定线段树的长度
Segment_Tree(int size)
...{
create(root, 1, size);
}
//接口
void insert (int a, int b)
...{
insert(root, a, b);
}
void remove (int a, int b)
...{
if (count(a, b) > 0)
remove(root, a, b);
}
int count(int a, int b)
...{
return count(root, a, b);
}
int measure(int a, int b)
...{
return measure(root, a, b);
}
int lines(int a, int b)
...{
return lines(root, a, b);
}
} ;
typedef struct node
... {
int l, r;
int count;
//lbd:
//左端点被覆盖为true,未被覆盖为false
//rbd相同
bool lbd, rbd;
node *left, *right;
} * nodeptr;
// 线段树
// 支持的操作有:
// insert(a, b) 插入一条线段[a,b]
// remove(a, b) 当[a, b]这条线段存在时,删除
// count(a, b) 统计[a, b]被覆盖的次数
// count(a, b)总是返回[a, b]中最少的覆盖次数,如
// 如果[a, b]仅被覆盖过一次,当[p, q]∈[a, b]
// 且[p, q]被覆盖,[a, p],[q, b]没有被覆盖时,
// count(a, b)只返回1
// measure(a, b) 返回[a, b]间的测度(测度m指的是区间中线段覆盖过的长度)
// lines(a, b) 返回[a, b]间连续线段的数量
// 几个例子:
// insert(3, 6), insert(4, 9), insert(11, 12)之后
// count(3, 6) = 1, count(4, 6) = 2, count(1, 4) = 0
// measure(1, 4) = 1, measure(3, 9) = 6, measure(5, 10) = 4
// lines(1, 9) = 1, line(5, 12) = 2
class Segment_Tree
... {
private:
nodeptr root;
//递归建树
void create(nodeptr& current, int a, int b)
...{
//初始化
current = new node;
current -> l = a; current -> r = b; current -> count = current -> lbd = current -> rbd = 0;
if (b - a == 1) //到达叶子,结束
current -> left = current -> right = NULL;
else //递归调用
...{
nodeptr lc, rc;
create(lc, a, (a+b) / 2);
create(rc, (a+b) / 2, b);
current -> left = lc;
current -> right = rc;
}
}
//插入一条线段
void insert(nodeptr p, int a, int b)
...{
//用于计算lines
p -> lbd |= (p -> l == a);
p -> rbd |= (p -> r == b);
//刚好是这一段,直接更新
if (p -> l == a && p -> r == b)
p -> count++;
else
...{
int m = (p -> l + p -> r) / 2; //a, b的中点
if (b <= m)
insert(p -> left, a, b); //需要在[a, m]插入[a, b]
else if (a >= m)
insert(p -> right, a, b); //在[m, b]中插入[a, b]
else //二分插入
...{
insert(p -> left, a, m);
insert(p -> right, m, b);
}
}
}
//删除一条线段
void remove(nodeptr p, int a, int b)
...{
//用于计算lines
p -> lbd &= !(p -> l == a);
p -> rbd &= !(p -> r == b);
//刚好是这一段,直接更新
if (p -> l == a && p -> r == b)
p -> count--;
else
...{
int m = (p -> l + p -> r) / 2; //a, b的中点
if (b <= m)
remove(p -> left, a, b); //需要在[a, m]删除[a, b]
else if (a >= m)
remove(p -> right, a, b); //在[m, b]中删除[a, b]
else //二分删除
...{
remove(p -> left, a, m);
remove(p -> right, m, b);
}
}
}
//统计[a,b]被覆盖的次数
//有待研究
int count(nodeptr p, int a, int b)
...{
if (p -> r - p -> l == 1)
return p -> count;
else
...{
int m = (p -> l + p -> r) / 2;
if (b <= m)
return count(p -> left, a, b) + p -> count;
else if (a >= m)
return count(p -> right, a, b) + p -> count;
else
return min(count(p -> left, a, m), count(p -> right, m, b)) + p -> count;
}
}
//计算[a,b]的测度
//结点的测度m指的是结点所表示区间中线段覆盖过的长度
//m = b - a (count > 0)
//m = 0 (count = 0且为叶结点)
//m = left.m + right.m (count = 0且为内部结点)
int measure(nodeptr p, int a, int b)
...{
if (count(a, b) > 0)
return (b - a);
if (b - a == 1)
return 0;
int m = (p -> l + p -> r) / 2;
return measure(p -> left, a, m) + measure(p -> right, m, b);
}
//计算[a,b]的连续线段数
//连续线段数line指的是区间中互不相交的线段条数
//计算方法:
//line = 1 (count > 0)
//line = 0 (count = 0且为叶结点)
//line = left.line + right.line - 1 (count = 0且为内部结点,且left.rbd和right.lbd同时为1)
//line = left.line + right.line (count = 0且为内部结点,且left.rbd和right.lbd不同时为1)
int lines(nodeptr p, int a, int b)
...{
if (count(a, b) > 0)
return 1;
if (b - a == 1)
return 0;
int m = (p -> l + p -> r) / 2;
return lines(p -> left, a, m) + lines(p -> right, m, b) - (p -> left -> rbd & p -> right -> lbd);
}
public:
//初始化时,指定线段树的长度
Segment_Tree(int size)
...{
create(root, 1, size);
}
//接口
void insert (int a, int b)
...{
insert(root, a, b);
}
void remove (int a, int b)
...{
if (count(a, b) > 0)
remove(root, a, b);
}
int count(int a, int b)
...{
return count(root, a, b);
}
int measure(int a, int b)
...{
return measure(root, a, b);
}
int lines(int a, int b)
...{
return lines(root, a, b);
}
} ;
当然,线段树还能实现别的操作,具体因题而异,大家可以自行扩充。