POJ 2528 Mayor's posters

Mayor's posters

给定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 ;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值