UVA10587 Mayor's posters

Mayor’s posters

题目大意是给你一个区间[1,10000000],然后有n个子区间,按照顺序填到[1,10000000]上,问你最后裸露在外的区间个数是几个。要注意的是这里的区间的端点不是一个点,而是单位长度,也就是说
[1,2]和[3,4]能够完全覆盖[1,4]。

做法是首先记录n个区间的左右端点,将其记录到point[]上(重复的去掉)。
然后我们要进行离散化。为什么呢?
因为在两个区间之间有许多空隙,比如[1000,2000]和[9000,10000],这2001到8999的段我们是不需要的,那么我们可以相对的做一个映射如下:
1000->1
2000->2
9000->3
10000->4
这样就可以把区间的范围缩小,减少运算量。

但是,只是这样普通地离散化是不够的
假设一种情况,

2
3
1 10
1 4
5 10
3
1 10
1 4
8 10

第一个case和第二个case映射完后都是1,2,3,4,但是显然第一个case的解是2,第二个case的解是3。问题就出在[1,4]和[5,10]的两个区间,4和5是连续的,而[1,4]和[8,10],4和8不是连续的,
我们在离散化的时候没有模拟出这种情况,如果原来的例子是1,4,7,8,10,(7是额外加的),那么我们可以在第二个例子里面,映射成1,2,3,4,5,区间还是[1,2]和[4,5],就解决了这个问题。为了方便,我们在1,4和8,10之间也加上3和9,这样写起代码来比较方便。

AC代码如下(C++编译器,G++没试过):

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn  = 10005;
struct node
{
    int l, r, lz;
} tr[16 * maxn];
int left[maxn], right[maxn], point[4 * maxn], vis[4 * maxn];
int cnt;
void lazy(int d)
{
    if(tr[d].lz != 0)
    {
        int ld = 2 * d;
        int rd = 2 * d + 1;
        tr[ld].lz = tr[d].lz;
        tr[rd].lz = tr[d].lz;
        tr[d].lz = 0;
    }
}
void build(int d, int l,int r)
{
    tr[d].l = l, tr[d].r = r;
    if(tr[d].l == tr[d].r)
    {
        tr[d].lz = 0;
        return ;
    }
    int mid = (l + r) / 2;
    int ld = 2 * d;
    int rd = 2 * d + 1;
    build(ld, l, mid);
    build(rd, mid + 1, r);
}
void update(int d, int l, int r, int val)
{
    if(tr[d].l == l && tr[d].r == r)
    {
        tr[d].lz = val;
        return;
    }
    lazy(d);
    int mid = (tr[d].l + tr[d].r) / 2;
    int ld  = 2 * d;
    int rd = 2 * d + 1;
    if(l > mid) update(rd, l, r, val);
    else if(r <= mid) update(ld, l, r, val);
    else
    {
        update(ld, l, mid, val);
        update(rd, mid + 1, r, val);
    }
}
void query(int d, int l, int r)
{
    if(tr[d].lz!= 0)
    {
        if(vis[tr[d].lz] == 0)
        {
            cnt++;
            vis[tr[d].lz] = 1;
        }
        tr[d].lz = 0;
        return ;
    }
    if(tr[d].l == tr[d].r)
    {
        return;
    }
    lazy(d);
    int mid = (tr[d].l + tr[d].r) / 2;
    int ld = 2 * d;
    int rd = 2 * d + 1;
    if(r <= mid) query(ld, l, r);
    else if(l > mid) query(rd, l, r);
    else
    {
        query(ld, l, mid);
        query(rd, mid + 1, r);
    }
}
int main()
{
    int cas;
    scanf("%d", &cas);
    while(cas --)
    {
        int n, np = 0;
        cnt = 0;
        scanf("%d", &n);
        for(int i = 1; i <= n; i++)
        {
            scanf("%d%d", &left[i], &right[i]);
            np ++;
            point[np] = left[i];
            np ++;
            point[np] = right[i];
        }
        sort(point + 1, point + np + 1);
        int mp = 1;
        for(int i = 2; i <= np; i++)
        {
            if(point[i] != point[i-1])
            {
                mp ++;
                point[mp] = point[i];
            }
        }
        for(int i = mp; i >= 2; i--)
        {
            if(point[i] != point[i-1] + 1)
            {
                mp ++;
                point[mp] = point[i-1] + 1;
            }
        }
        sort(point + 1, point + 1 + mp);
        memset(tr, 0, sizeof(tr));
        memset(vis, 0, sizeof(vis));
        build(1, 1, mp);

        for(int i = 1; i <= n; i++)
        {
            int l = lower_bound(point + 1, point + mp + 1, left[i]) - point;
            int r = lower_bound(point + 1, point + mp + 1, right[i]) - point;
            update(1, l, r, i);
        }
        query(1, 1, mp);
        printf("%d\n", cnt);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值