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

http://poj.org/problem?id=2528


这题一定要写个题解。Orz。
不信看图,五月多做过一次,一直RE。现如今来做,仍然RE多发。


这里写图片描述


题意:在墙壁上贴广告,广告的版面有大有小,并且贴广告有先后之分,后面贴的广告会覆盖前面的广告,求解最后能看到的广告面。

本题数据太大,要用到离散化,意思是将区间范围很大的数据集映射到较小的数据集。
离散化的步骤:

  • 读取一对区间(x,y),用一个结构体(这里用arr)来存储这些点:arr[i].st =x,arr[i].ed=y;同时用另外一个离散的数组has来存储这些点,has[t++]=x,has[t++]=y;
  • 把has数组sort一遍
  • 把has数组unique(去重)一遍
    int tt=unique(has,has+t)-has;
  • 接下来更新线段树的时候,每个值通过二分得到:
    int xx= lower_bound(has,has+tt,arr[i].st)
    int yy= lower_bound(has,has+tt,arr[i].ed)
    update(xx,yy);

  • 然后就没有然后了 ==


只不过这题的不同的是,题目给的数字代表的是一个单位长度,而不是具体的一个点,普通的离散化会造成错误。所以在区间长度大于1的时候,往往在区间多加一个点。我省掉了这个步骤,我是在不管是否大于1,区间都在左右端点分别再加点。
这题让我RE的不是数组开大开小的问题,是一个query懒人操作没有处理好的问题。
错错更健康,以后不再错就好 ==


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

#define mset(x,y)  memset(x,y,sizeof(x))
#define lson l,mid,i<<1
#define rson mid+1,r,i<<1|1

using namespace std;
const int N = 10000 +10;
int ans;
struct Node
{
    int st,ed;
}arr[N];//记录区间的起点和终点
bool vis[N];
int has[N*4],tr[N*16];//注意数组范围,一定要开这么大
void built(int l,int r,int i)
{
    tr[i]=0;
    if(l==r)  return;

    int mid=(l+r)>>1;
    built(lson);
    built(rson);
    return;
}
void pushdown(int i)
{
    tr[i<<1] = tr[i<<1|1] = tr[i];
    tr[i] = 0;
    return;
}
void update(int l,int r,int i,int a,int b,int x)
{
    if(l>=a&&r<=b)
    {
        tr[i] = x;
        return;
    }

    if(tr[i]) pushdown(i);
    int mid=(l+r)>>1;
    if(mid>=a)  update(lson,a,b,x);
    if(mid<b) update(rson,a,b,x);
    return;
}
void query(int l,int r,int i,int a,int b)
{
    if(l>=a && r<=b &&tr[i]!=0)
    {       
        if(vis[tr[i]]) ans++,vis[tr[i]]=false;
        return;
    }
    if(l==r) return;//RE多发的关键处,Orz!!!
    if(tr[i]) pushdown(i);
    int mid= (l+r)>>1;
    if(mid>=a) query(lson,a,b);
    if(mid<b) query(rson,a,b);
    return;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        mset(has,0);
        int t=0;
        for(int i=0;i<n;i++)
        {
            scanf("%d%d",&arr[i].st,&arr[i].ed);
            has[t++]=arr[i].st;
            has[t++]=arr[i].st+1;//奇妙的地方
            has[t++]=arr[i].ed;
            has[t++]=arr[i].ed-1;//奇妙的地方
        }
        sort(has,has+t);
        int tt=unique(has,has+t)-has;
        built(1,tt,1);
        mset(vis,true);
        ans=0;
        for(int i=0;i<n;i++)
        {
            int x = lower_bound(has,has+tt,arr[i].st) - has + 1;//数组是从0开始的,所以+1保证正数性
            int y = lower_bound(has,has+tt,arr[i].ed) - has + 1;
            update(1,tt,1,x,y,i+1);
        }
       query(1,tt,1,1,tt);
       printf("%d\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值