POJ 2528 Mayor's posters(线段树成段更新+离散化)

题目链接: http://poj.org/problem?id=2528

题目大意,就是在大的区间贴海报,每次给一个小区间贴海报,问最后可见(一点点也算)的海报有几种。

线段树处理区间更新的做法是对的,每给定一个区间,然后成段更新,更新值为i,i为第i张海报,然后用一个vis数组标记出现的,就可以统计有多少个不同的海报了。

但是这题的区间比较大,直接处理会超时,于是学习了离散化的处理方法,其实不用想太多,就是把区间端点进行映射。

比如测试样例:

1
5
1 4
2 6
8 10
3 4
7 10
端点:1 2 3 4 6 7 8 10

映射:1 2 3 4 5 6 7  8

这样在覆盖区间[1,8]时,我们就可以映射着覆盖[1,7]了。如果只有10000张海报,那么即使每个端点的值都不一样,最多也就20000个端点,每个端点做一个映射。覆盖的区间范围就大大缩小了。

只做这一个映射就可以ac了,但是是不严谨的,我也是看了讨论版才知道的。

举个例子

[1,10], [1, 4], [6,10]

1 4 6 10

1 2  3  4
即X[1] = 1, X[2] = 4, X[3] = 6, X[4] = 10
手工模拟覆盖会发现颜色少了一种,即5的海报颜色丢失了。

解决方案:给两个不连续点之间插入一个点,保证不出现原本不连续的区间被当成连续区间。

#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<algorithm>
#include <queue>
using namespace std;

const int maxn = 40005; // 10000张海报、每张海报两个点×2 不连续点间插点×2
const int oo = 0xfffffff;
#define L_CHILD rt<<1, begin, mid
#define R_CHILD rt<<1|1, mid + 1, end
#define LL long long
int segTree[maxn<<2];
int n;
int vis[maxn];
int l[maxn], r[maxn], node[maxn<<2];

void pushDown(int rt) {
    if(segTree[rt]) {
        segTree[rt<<1] = segTree[rt<<1|1] = segTree[rt];
        segTree[rt] = 0;
    }
}

void update(int rt, int begin, int end, int left, int right, int val) {
    if(left > end || right < begin) {
        return ;
    }
    if(left <= begin && end <= right) {
        segTree[rt] = val;
        return ;
    }
    pushDown(rt);
    int mid = begin + end >> 1;
    update(L_CHILD, left, right, val);
    update(R_CHILD, left, right, val);
}
int query(int rt, int begin, int end) {
    if(segTree[rt]) {
        if(!vis[segTree[rt]]) {
            vis[segTree[rt]] = 1;
            return 1;
        }
        return 0;
    }
    if(begin == end) {
        return 0;
    }
    int mid = begin + end >> 1;
    return query(L_CHILD) + query(R_CHILD);
}
int main() {
//    freopen("test.txt", "w", stdout);
    int t, n, x, y;
    scanf("%d", &t);
    while (t --) {
        memset(vis, 0, sizeof(vis)), vis[0] = 1;
        memset(segTree, 0, sizeof(segTree));
        scanf("%d", &n);
        int k = 1, m = 2;
        for (int i = 1; i <= n; i ++) {
            scanf("%d%d", l+i, r+i);
            node[k++] = l[i];
            node[k++] = r[i];
        }
        // 离散化后 去重
        sort(node + 1, node + k);
        for (int i = 2; i < k; i ++) {
            if(node[i] != node[i-1]) {
                node[m++] = node[i];
            }
        }
        // 避免在出现[1,10], [1, 4], [6,10]
        // 离散化: X[1] = 1, X[2] = 4, X[3] = 6, X[4] = 10
        // 5的海报颜色丢失,解决:给两个不连续点之间插入一个点
        for (int i = m - 1; i > 1; i --) {
            if(node[i] != node[i-1] + 1) {
                node[m++] = node[i-1] + 1;
            }
        }
        sort(node + 1, node + m);
        for (int i = 1; i <= n; i ++) {
            int a = lower_bound(node + 1, node + m, l[i]) - node;
            int b = lower_bound(node + 1, node + m, r[i]) - node;
            update(1, 1, m-1, a, b, i);
        }
        printf("%d\n", query(1, 1, m));
    }
    return 0;
}
参考: http://poj.org/showmessage?message_id=348360
            http://www.zgxue.com/197/1977450.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值