POJ 2528 经典!线段树离散化

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

题意:n(n<=10000)个人依次贴海报,给出每张海报所贴的范围li,ri(1<=li<=ri<=10000000)。求出最后还能看见多少张海报。


解法:离散化,如下面的例子(题目的样例),因为单位1是一个单位长度,将下面的

      1   2   3   4  6   7   8   10

     —  —  —  —  —  —  —  —

      1   2   3   4  5   6   7   8

离散化  X[1] = 1; X[2] = 2; X[3] = 3; X[4] = 4; X[5] = 6; X[7] = 8; X[8] = 10

于是将一个很大的区间映射到一个较小的区间之中了,然后再对每一张海报依次更新在宽度为1~8的墙上(用线段树),最后统计不同颜色的段数。

但是只是这样简单的离散化是错误的,

如三张海报为:1~10 1~4 6~10

离散化时 X[ 1 ] = 1, X[ 2 ] = 4, X[ 3 ] = 6, X[ 4 ] = 10
第一张海报时:墙的1~4被染为1;
第二张海报时:墙的1~2被染为2,3~4仍为1;
第三张海报时:墙的3~4被染为3,1~2仍为2。
最终,第一张海报就显示被完全覆盖了,于是输出2,但实际上明显不是这样,正确输出为3。

新的离散方法为:在相差大于1的数间加一个数,例如在上面1 4 6 10中间加5(算法中实际上1,4之间,6,10之间都新增了数的)

X[ 1 ] = 1, X[ 2 ] = 4, X[ 3 ] = 5, X[ 4 ] = 6, X[ 5 ] = 10

这样之后,第一次是1~5被染成1;第二次1~2被染成2;第三次4~5被染成3

最终,1~2为2,3为1,4~5为3,于是输出正确结果3。


我是这样理解这个离散化为啥要加中间一个点:如果贴海报只有两个端点很容易出错,但如果中间有一个点会和两个端点有一样的颜色,统计的时候就不会出错(个人想法!!!)


#include <iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
using namespace std;
const int maxn=10100;

#define lson node<<1,l,mid
#define rson node<<1|1,mid+1,r
int x[maxn<<4];
int tree[maxn<<4],ans=0,n=0,num=1;
bool hash[maxn<<2];
int lx[maxn<<2],rx[maxn<<2];

void pushdown(int node)
{
    tree[node<<1]=tree[node<<1|1]=tree[node];
    tree[node]=-1;
}

void update(int node ,int l,int r,int begin, int end,int x)
{
    if(begin<=l&&end>=r)
    {
          tree[node]=x; return;
    }
    if(tree[node]!=-1) pushdown(node);

    int mid=(l+r)>>1;
    if(mid>=begin) update(lson,begin,end,x);
    if(mid<end) update(rson,begin,end,x);
}

void query(int node ,int l,int r)
{
    if(l==r)
    {
       if(hash[tree[node]]==0)
       {
           hash[tree[node]]=1;  ans++;
       }
       tree[node]=-1;
       return;
    }

    if(tree[node]!= -1) pushdown(node);
    int mid=(l+r)>>1;
    query(lson); query(rson);
}

int bsearch(int ll, int rr,int xx)
{
    int mm;
    while(ll<=rr)
    {
        mm=(ll+rr)>>1;
        if(x[mm]==xx) return mm;
        else if(x[mm]>xx) rr=mm-1;
        else ll=mm+1;
    }
    return ll;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(tree,-1,sizeof(tree));
        memset(hash,0,sizeof(hash));
        int cnt=0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&lx[i],&rx[i]);
            x[++cnt]=lx[i];
            x[++cnt]=rx[i];
        }

        sort(x+1,x+1+cnt);
        num=1;
        for(int i=2;i<=cnt;i++)
        {
            if(x[i]!=x[i-1]) x[++num]=x[i];
        }
        for(int i=num;i>1;i--)
        {
           if(x[i]-x[i-1]>1) x[++num]=x[i]-1;
        }
        sort(x+1,x+1+num);

        for(int i=1;i<=n;i++)
        {
            int l=bsearch(1,num,lx[i]);
            int r=bsearch(1,num,rx[i]);
            update(1,1,num,l,r,i);
        }

        ans=0;
        query(1,1,num);
        printf("%d\n",ans);
    }
    return 0;
}


个人感觉大佬离散化代码实现写的特别棒 orz!!!

还有一个点 : 原来自己写二分的时候条件 写的是 while(l<r) 其实这样会取不到右端点的!!!对二分还是不熟练






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值