poj_2528_线段树+离散化+lazy

题目

  • 题目链接:http://poj.org/problem?id=2528
  • 题意:第一行T:测试用例数量;第二行n,海报的数目;接下来n行,l, r,:【l, r】区间被一张海报覆盖;问最终能看到多少的海报

思路

  • 典型的线段树,区间覆盖。不过这里(l, r)的最大值是1千万, built(1, 一千万, 1), 肯定会超时,要将坐标离散化;
  • 离散化: 比如原题测试样例n张海报区间时:
    (1, 4), (2, 6), (8, 10), (3, 4), (7, 11);
    在这里插入图片描述
  • 离散化处理完成后就是常规的区间覆盖了

代码

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;
const int maxn = 200005;
bool vis[maxn];
int LL[maxn], RR[maxn];
int x[maxn << 2];
int cnt;
struct{
    int l, r;
    int num;//num表示海报 num>0表示【l,r】区间被海报num覆盖,num==-1表示【l,r】有超过两种海报
}tree[maxn << 2];

int lower_bound(int l, int r, int key){//二分查找下标,最后的离散化数组与原来的实际上是下标对应关系,原来的值对应离散在新数组中的值的下标
    int mid = (l + r) / 2;
    if(x[mid] == key) return mid;
    else if(key < x[mid]) return lower_bound(l, mid - 1, key);
    else return lower_bound(mid + 1, r, key);
}

void built(int s, int e, int node){

    tree[node].l = s;
    tree[node].r = e;
    tree[node].num = 1;
    int mid = (s + e) >> 1;
    if(s == e){
        return ;
    }
    built(s, mid, node << 1);
    built(mid + 1, e, node << 1 | 1);
}

void update(int L, int R, int node, int num){
    if(tree[node].l >= L && tree[node].r <= R){
        tree[node].num = num;
        return ;
    }else{
        if(tree[node].num != -1){//lazy的pushdown操作
            tree[node << 1].num = tree[node].num;
            tree[node << 1 | 1].num = tree[node].num;
            tree[node].num = -1; 
        }
        int mid = (tree[node].l + tree[node].r) >> 1;
        if(mid < L) update(L, R, node << 1 | 1, num);
        else if(mid >= R) update(L, R, node << 1, num);
        else{
           update(mid | 1, R, node << 1 | 1, num);
           update(L, mid, node << 1, num);
        }
    }
}
void query(int L, int R, int node){
    if(tree[node].num != -1){
        vis[tree[node].num] = true;//【l, r】间的tree[node].num!=-1,说明这个区间都是一种海报,否则向下查找
        return;
    }else{
        int mid = (tree[node].l + tree[node].r) >> 1;
        if(L > mid){
            query(L, R, node << 1 | 1);
        }else if(R <= mid){
            query(L, R, node << 1);
        }else{
            query(L, mid, node << 1);
            query(mid + 1, R, node << 1 | 1);
        }
    }
}
int main(){
    int t;
    cin >> t;
    while(t --){
        memset(vis, false, sizeof(vis));
        int ans = 0, n, cnt = 0;
        cin >> n;
        for(int i = 0; i < n; i++){
            scanf("%d %d", &LL[i], &RR[i]);
            x[++cnt] = LL[i];
            x[++cnt] = RR[i];
        }
        sort(x + 1, x + cnt + 1);
        // for(int i = 1; i <= cnt; i++){
        //     cout << "x[" << i << "]= " << x[i] << endl;
        // }
//去重
        int count = 1;
        for(int i = 2; i <= cnt; i++){
            if(x[i - 1] == x[i]){
                while(x[i - 1] == x[i]){
                    i++;
                }
                i--;
            }else{
                x[++count] = x[i];
            }
        }
        // for(int i = 1; i <= count; i++){
        //     cout << "x[" << i << "]= " << x[i] << endl;
        // }
//不能让不是领居的变成邻居,正确的离散处理
        cnt = count;
        for(int i = 2; i <= count; i++){
            if(x[i] - x[i - 1] > 1) x[++ cnt] = x[i - 1] + 1;
        }
        count = cnt;
        sort(x + 1, x + count + 1);
        // for(int i = 1; i <= count; i++){
        //     cout << "x[" << i << "]= " << x[i] << endl;
        // }

        built(1, count, 1);
        for(int i = 0; i < n; i++){
            int l = lower_bound(1, count, LL[i]);
            int r = lower_bound(1, count, RR[i]);
            //cout << l << r << endl;
            //区间覆盖
            update(l, r, 1, i + 1);
        }
        //查询
        query(1, count, 1);
        for(int i = 1; i <= n; i++){
            if(vis[i]) ans ++;
        }
        cout << ans << endl;
    }
    //cin >> t;
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值