问题:直线上分布一系列的整数格点1,2,…,n,有m个整数线段[ai,bi](1<=ai<=bi<=n,i=1,...,m),求q个区间[sj,tj]分别被线段覆盖的总长度,即[ai,bi]的并集中包含的[sj,tj]中整数格点数。
例如:n=12,m=5,m个线段是[1,5], [4,6], [9,9], [1,2], [8,10],
则[1,12]被覆盖总长度是9,而[2,9]被覆盖总长度为7。
问题 :直线上分布一系列的整数格点1,2,…,n,有m个整数线段[ai,bi](1<=ai<=bi<=n,i=1,...,m),求任意一个区间[sj,tj]被这些线段覆盖的总长度,即它们的并集包含的整数格点个数。
思路:
首先在附加数据域设一个cover标记来跟踪一个区间是否被全覆盖,也可再设一个length跟踪覆盖长度(本例不设)。Node如下:
struct Node {
int left, right;
int cover; // 标记, 1表示被全覆盖
};
然后实现三个模块:建立、更新、查询。
建立:建立线段树,顺便将cover初值置0。
更新:根据每个线段[ai,bi]对线段树进行更新,记录覆盖情况。
查询:对任意区间的查询,由它的拼接元线段和标记返回查询结果。
样例输入:
12 5 2
1 5
4 6
9 9
1 2
8 10
1 12
2 9
样例输出:
9
7
code:
采用左闭右开的区间表示
#include <iostream>
const int MAXN =1000;
struct node{
//覆盖范围
int cover;
//左区间闭区间
int left;
//右区间开
int right;
};
//最多四倍空间
node st[MAXN<<2];
// 节点建树
//start 开始的建树区间
//end 末尾区间
//其区间对应的节点id
void Build(int start,int end,int id){
st[id].cover = 0;
st[id].left = start;
st[id].right = end;
//结束条件,因为左闭右开,所以当插值为1的时候已经到子节点了 比如 [3,4),其实对应的就是节点3而已。
if(end - start == 1)return;
int mid = (start+end)/2;
Build(start,mid,id*2);
Build(mid,end,id*2+1);
}
// 节点更新
//start 开始的更新间
//end 末尾更新区间
//其区间对应的节点id
//注意,这个时候线段树已经建好了,对应的区间是要更新的区间,而不是线段树每一个节点的区间
void Update(int start,int end,int id){
//想要更新的区间在这个子树的节点范围之外
if(end<st[id].left || start>st[id].right)return;
//想要更新的区间正好覆盖了这个子树的范围,那么就可以更新
if(start<=st[id].left && end>=st[id].right)st[id].cover = 1;
//退出条件,如果是子节点了,就不要再递归了
if(st[id].right - st[id].left ==1) return;
Update(start,end,id*2);
Update(start,end,id*2+1);
}
// 节点查询
//start 开始的查询间
//end 末尾的查询区间
//其区间对应的节点id
//注意,这个时候线段树已经建好了,对应的区间是要更新的区间,而不是线段树每一个节点的区间
int Query(int start,int end,int id){
if(end<st[id].left || start>st[id].right)return 0;
if(start<=st[id].left && end>=st[id].right &&st[id].cover == 1){
// printf("left:%d right:%d\n", st[id].left,st[id].right);
return st[id].right - st[id].left;
}
if(st[id].right - st[id].left ==1) return 0;
int ans = 0;
ans+=Query(start,end,id*2);
ans+=Query(start,end,id*2+1);
return ans;
}
int main(int argc, char** argv) {
int n, m, q;
int a, b, s, t;
scanf("%d%d%d", &n, &m, &q);
//左臂右开,所以建树范围是[1,n+1) 起点是根节点1
Build(1, n+1,1);
for (int i = 0; i < m; i++) {
scanf("%d%d", &a, &b);
//左臂右开,所以范围是[a,b+1) 起点是根节点1
Update(a, b+1,1);
}
for (int i = 0; i < q; i++) {
scanf("%d%d", &s, &t);
//左臂右开,所以范围是[s,t+1) 起点是根节点1
printf("%d\n", Query( s, t+1,1));
}
return 0;
}