给定n条线段,线段端点的范围从1-10000000,范围比较大,如果直接用线段树,肯定会爆内存。需要离散化操作,这是我的首个离散化题目,由于边只有100000左右也就是说对应的点只有200000个左右,如果只考虑这200000个点就可以保证不会爆内存了。我们将输入的边,离散化,将对应的坐标重新分配到 1 ~ 2 * n上。然后再按照线段树的操作进行插入与查询。代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std ;
#define MAXN 20005
#define L(x) x<<1
#define R(x) x<<1|1
#define Mid(x , y) (x+y)>>1
struct Node{
int l ;
int r ;
int f ;
}tree[MAXN * 4] ;
struct MAP{
int l ;
int end ;
int f ;
}map[MAXN] ;
int n ;
//离散后的线段
int line[MAXN][2] ;
bool flagc[MAXN] ;
bool cmp(MAP a , MAP b){
return a.end < b.end ;
}
//构建线段树
void build(int l , int r , int c){
tree[c].l = l ;
tree[c].r = r ;
tree[c].f = 0 ;
if(l == r){
return ;
}
//构建当前节点的左子树
build(l , Mid(l , r) , L(c)) ;
//构建当前节点的右子树
build((Mid(l , r)) + 1 , r , R(c)) ;
}
//x ,y 表示区间,in表示区间的标号c代表当前的节点
void insert(int x , int y , int in , int c){
//如果线段覆盖当前区间则将其覆盖
if(x <= tree[c].l && y >= tree[c].r){
tree[c].f = in ;
return ;
}
//如果当前区间已被覆盖
if(tree[c].f > 0){
//向下传递父节点的信息
tree[ L(c) ].f = tree[c].f ;
tree[ R(c) ].f = tree[c].f ;
tree[c].f = 0 ;
}
int mid = Mid(tree[c].l , tree[c].r) ;
if(y <= mid){
insert(x , y , in , L(c) ) ;
}
else if(x > mid){
insert(x , y , in , R(c) ) ;
}
else {
//插入到左边
insert(x , mid , in , L(c) ) ;
//插入到右边
insert(mid + 1 , y , in , R(c) ) ;
}
}
void countLine(int c , int &ans){
if(tree[c].f > 0){
if(!flagc[tree[c].f]){
ans ++ ;
flagc[tree[c].f] = 1 ;
}
return ;
}
if(tree[c].l==tree[c].r){
return ;
}
countLine(L(c) , ans) ;
countLine(R(c) , ans) ;
}
int main(){
int t ;
scanf("%d" , &t) ;
while(t--){
int x ;
int y ;
memset(flagc , 0 , sizeof(flagc)) ;
scanf("%d" , &n) ;
for(int i = 1 ; i <= n ;i ++){
scanf("%d %d" , &x , &y) ;
map[2 * i - 1].l = i ;
map[2 * i - 1].end = x ;
map[2 * i - 1].f = 1 ;
map[2 * i].l = i ;
map[2 * i].end = y ;
map[2 * i].f =0 ;
}
//按照从小到达进行排序
sort(map + 1 , map + 1 + 2*n , cmp) ;
//离散化处理
int t = 1 ;
int temp = map[1].end ;
for(int i = 1 ; i <= 2 * n ; i ++){
if(temp != map[i].end){
t ++ ;
temp = map[i].end ;
}
//将原来的线段的端点重新组合到新的线段中
if(map[i].f == 1){
line[map[i].l][0] = t ;
}
else {
line[map[i].l][1] = t ;
}
}
build(1 , 2 * n , 1) ;
for(int i = 1 ; i <= n ; i ++){
insert(line[i][0] , line[i][1] , i , 1) ;
}
//统计
int ans = 0 ;
countLine(1 , ans) ;
printf("%d\n" , ans) ;
}
return 0 ;
}

本文介绍了一种利用离散化处理大规模线段范围问题的方法,通过将线段端点映射到较小范围内,避免了内存溢出的问题,并详细展示了如何构建线段树并实现插入与查询操作。

被折叠的 条评论
为什么被折叠?



