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

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

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

题意:

        那个城市里要竞选市长,然后在一块墙上可以贴海报为自己拉票,每个人可以贴连续的一块区域,后来帖的可以覆盖前面的,问到最后一共可以看到多少张海报。整块墙可以看成一个数轴,每张海报就是数轴上的一个区间。

分析:

       首先题目的区间范围高达1000W,如果直接计算可能超内存且超时间。所以需要离散化。

       离散化一:比如对于如下区间集合,[1,1000],[500,2000],[1500,2500].那么把所有区间端点1,500,1000,1500,2000,2500离散化后就是1,2,3,4,5,6.离散化后所得区间为:[1,3],[2,5],[4,6].可以知道离散化前可见区间有3个,但是离散化后只有区间[1,3]和区间[4,6]可见.所以离散化一的方式是有问题的(事后分析,其实我们不需要用到空白区域的信息,所以就算用此方式离散化,最终得到的也是正确结果)。

       离散化二:对于区间端点的离散化,如果离散化之前相邻的两个数不是类似于a与a+1的差距1关系,那么就自动在后面的这个数的离散化结果上加1.比如:

[1,10],[1,5],[7,10] 离散化后的区间为[1,7][1,3],[5,7]

离散化方式二主要就是让本来不相邻的数继续保持不相邻即可.

总的处理逻辑是:先读入所有区间,然后把区间端点离散化到map中,然后求出所有新的区间(范围变小了),然后再一次set线段树操作,最后求出线段树中一共有多少种不同的值即可.

另外特别需要注意的:

1.本题如果开头少了int li[MAXN],ri[MAXN];这句话就直接从AC变成运行时出错.虽然我代码中根本没有用到这两个数组,逆天!.

2.本题如果用map来离散化数据,然后查找就会超时.我代码中用的方法是先把所有可能的数读入一个数组num中,然后排序,然后用二分查找原来的值x再num中的位置i,那个位置就是i就是x的新值.注意,由于不相邻的数我们重新映射出来的数也要不相邻,所以这里如果num[i]和num[i-1]不相邻,那么我就插入一个num[i]-1到num数组后面去,这样我们生成的新数值也不相邻.

AC代码:79ms

<span style="font-size:18px;">#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define lson i*2,l,m
#define rson i*2+1,m+1,r
const int MAXN=11111;
bool vis[MAXN*3];
int vis_num;
int li[MAXN],ri[MAXN];//我后面没有用到这两个变量,但是如果注释掉这句就是RE,不注释就是AC,逆天

int setv[MAXN*16];
int num[MAXN*3];
void PushDown(int i)
{
    if(setv[i]!=-1)
    {
        setv[i*2]=setv[i*2+1]=setv[i];
        setv[i]=-1;
    }
}
void update(int ql,int qr,int v,int i,int l,int r)
{
    if(ql<=l&&r<=qr)
    {
        setv[i]=v;
        return ;
    }
    PushDown(i);
    int m=(l+r)/2;
    if(ql<=m) update(ql,qr,v,lson);
    if(m<qr) update(ql,qr,v,rson);
}

void query(int i,int l,int r)
{
    if(setv[i]!=-1)
    {
        if(vis[setv[i]]==false)
        {
            vis_num++;
            }
            vis[setv[i]]=true;

        return ;
    }
    if(l==r)return ;
    int m=(l+r)/2;
    query(lson);
    query(rson);
}


struct node
{
    int l,r;
}nodes[MAXN];
int bin(int key,int n,int num[])
{
    int l=0,r=n-1;
    while(r>=l)
    {
        int mid=(r+l)/2;
        if(num[mid]==key) return mid;
        if(num[mid]>key)
            r=mid-1;
        else if(num[mid]<key)
            l=mid+1;

    }
    return -1;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int nn,m;
        int n;
        scanf("%d",&n);
        nn=0;
        for(int i=0;i<n;i++)
        {
            scanf("%d%d",&nodes[i].l,&nodes[i].r);
            num[nn++]=nodes[i].l;
            num[nn++]=nodes[i].r;
        }
        sort(num,num+nn);
        m=1;
        for(int i=1;i<nn;i++)//去重复的值
            if(num[i]!=num[i-1])num[m++]=num[i];
        for(int i=m-1;i>0;i--)//如果存在比如1 和3 或3和5 这种相邻值,那么就在序列末尾插入2或 4这种中间值
            if(num[i]!=num[i-1]+1)num[m++]=num[i-1]+1;
        sort(num,num+m);
        memset(setv,-1,sizeof(setv));
        for(int i=0;i<n;i++)
        {
            nodes[i].l=bin(nodes[i].l,m,num);
            nodes[i].r=bin(nodes[i].r,m,num);
            //printf("%d %d\n",nodes[i].l,nodes[i].r);
            update(nodes[i].l,nodes[i].r,i,1,0,m-1);
        }
        vis_num=0;
        memset(vis,0,sizeof(vis));
        query(1,0,m-1);

        printf("%d\n",vis_num);
    }
    return 0;
}
</span>


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值