POJ 2528 经典!线段树离散化

来源:互联网 发布:淘宝上哪家玉石店靠谱 编辑:程序博客网 时间:2024/06/06 02:34

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,rint 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) 其实这样会取不到右端点的!!!对二分还是不熟练






0 0