线段树专题 D - Mayor's posters (离散化 贴海报)

D - Mayor's posters

题目链接

题意:

在宽度为10000000的墙壁上,按顺序贴n张海报(1 <= n <= 10000),每种海报的颜色不同,颜色用数字表示,且第 i 张海报的颜色就是 i ,给出海报的左右坐标。之后贴上的海报可以覆盖之前贴上的海报。问:在贴完所有n张海报之后,能从墙上看到几种颜色。

输入:

先输入一个T(1),表示测试样例的数量
接着输入一个n(5),表示海报的数量
接下来的n(5)行,每行代表一张海报,包括两个整数,分别代表海报的左端点与右端点。
1
5
1 4
2 6
8 10
3 4
7 10

输出:

4

思路:

因为刚刚学过线段树的区间更新,所以刚刚看到题目,觉得这道题用线段树是可以写的。但是又发现墙壁的长度为10000000。显然,直接用这样的长度建树不仅内存需要巨大,而且极易超时。

所以需要用离散来处理一下。因为海报数量是小于10000的,海报有左右两个坐标点,所以说本题最多只有20000个点。此处的离散比较简单。意思是用小的数来替换大的数,例如一些点的坐标为:100、1000、1111、9999。我们可以用1来代替100,2来代替1000,3来代替1111,4来代替9999。这样一来,无论在还未离散时,坐标能达到10000000,但在离散后,坐标最大只能达到20000了。

离散过程如下:

struct Poster
{
    int x;
    int id;
}post[maxn * 4];

int cmp1(Poster a , Poster b)
{
    return a.x < b.x;
}


sort (post + 1  , post + n * 2 + 1 , cmp1);
int last = 0;
int ans = 0;
for (int i = 1 ; i <= n * 2 ; i++)
{
    if (post[i].x == last)
        post[i].x = ans;
    else
    {
        last = post[i].x;
        post[i].x = ++ans;
    }
}

在代码中,我用结构体Poster类的post [ 20000 ] 数组来储存点的信息,x代表点的坐标,id代表点属于的海报的颜色。我们首先需要把所有点按坐标,从小到大排序。接着我们定义一个last用于存前一个点在离散之前的坐标,再定义一个ans用来做点的新坐标,如果前后两个点在未离散时的坐标相同,则前后两个点的新坐标相同。如果前后两个点在未离散时的坐标不相同,则后面点的新坐标比前面点的新坐标大1。这样一来我们就完成了离散。

离散过后。我们需要开始建树,然后用区间更新的方法把海报的颜色更新到树上。

此处我想要将海报从后往前地进行更新。更新时,如果发现此处已经有颜色,那么就不进行更新。因为后面的海报是会覆盖前面的海报的。如果这张海报能够有地方上色,说明在最后,我们是能够看到这张海报的,此时我们用一个flag来进行标记,如果上色,flag = 1,如果flag = 1 ,那么我们最后的答案 result ++ 。所以我们需要做下面几件事:

  1. 先对离散后的点按照从 id 大到 id 小的顺序排序。
  2. 建树
  3. 用海报对树更新,同时计算result
  4. 输出答案

代码如下:

#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <cstring>

using namespace std;

const int maxn = 1e5 + 5;

struct Poster
{
    int x;
    int id;
}post[maxn * 4];

int flag;

int cmp1(Poster a , Poster b)
{
    return a.x < b.x;
}

int cmp2(Poster a , Poster b)
{
    if (a.id == b.id)
        return a.x < b.x;
    return a.id > b.id;
}

struct Tree
{
    int lson;
    int rson;
    bool vis;
}tree[maxn * 4 ];

void Build (int l , int  r , int h)
{
    tree[h].lson = l;
    tree[h].rson = r;
    tree[h].vis = 0;
    if (l == r)
        return ;
    int mid = (l + r) >> 1;
    Build (l  , mid , h << 1);
    Build ( mid + 1 , r , h << 1 | 1);
}

void Push_Up(int h)
{
    tree[h].vis = tree[h << 1].vis && tree[h << 1 | 1].vis;
}

void Query (int l , int r , int h)
{
    if (tree[h].vis)
        return ;

    if (l == tree[h].lson && r == tree[h].rson)
    {
        flag = 1;
        tree[h].vis = 1;
        return;
    }

    int mid = (tree[h].lson + tree[h].rson) >> 1;

    if (l > mid)
        Query (l , r , h << 1 | 1);

    else if (r <= mid)
        Query (l , r , h << 1);

    else
    {
        Query (l , mid , h << 1 );
        Query (mid + 1 , r , h << 1 | 1);
    }
    Push_Up(h);
}

int main ()
{
    int T;
    scanf ("%d" , &T);
    while (T--)
    {
        int n ;
        scanf("%d" , &n);
        for (int i = 1 ; i <= n * 2 - 1 ; i+=2)
        {
            scanf ("%d%d" , &post[i].x , &post[i + 1].x);
            post[i].id = post[i + 1].id = i;
        }
        sort (post + 1  , post + n * 2 + 1 , cmp1);
        int last = 0;
        int ans = 0;
        for (int i = 1 ; i <= n * 2 ; i++)
        {
            if (post[i].x == last)
                post[i].x = ans;
            else
            {
                last = post[i].x;
                post[i].x = ++ans;
            }
        }
        Build (1 , n * 2 , 1);
        sort (post + 1  , post + n * 2 + 1 , cmp2);
        int result = 0;
        for (int i = 1 ; i <= n * 2 - 1 ; i+=2)
        {
            flag = 0;
            Query (post[i].x , post[i + 1].x , 1);
            if (flag) result++;
        }
        printf ("%d\n" , result);
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值