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

来源:互联网 发布:老外中国快递知乎 编辑:程序博客网 时间:2024/05/21 17:28

这道题目让我又重新认识了一下离散化:

首先总结一下离散化的特点:

1)有时区间的端点并不是整数,或者区间太大导致建树内存开销过大而MLE,那么就需要进行离散化后再建树。

2)意思是将区间范围很大的数据集映射到较小的数据集,这样建树更加有效,或者说我们只取需要的值来用。

这个意思说到底就是进行映射,把原来很大的映射到一个较小的空间中去。

题意:

给定一些海报,它们可能相互重叠,告诉你每个海报的宽度(它们的高度都是一样的)和先后的叠放次序,问没有被完全盖住的海报有多少张?

这里我们注意到了数据的范围:海报最多100000张,而墙的范围最大是10的7次。所以我们肯定不能以墙来建树。(而且这里海报都是完全覆盖每一个墙面的)

所以我们要把海报的左右端点进行离散化,也就是把原先很大的每个端点进行标号,把它转化为更小的数据

这里要注意一点就是:当它们之间距离等于1的时候,只要+1就可以了,但是当距离大于1的时候,那么要+2

原因:(来源于http://blog.csdn.net/non_cease/article/details/7383736)

解法:离散化,如下面的例子(题目的样例),因为单位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<stdio.h>#include<string.h>#include<iostream>#include<algorithm>#include<vector>#include<set>#include<map>#include<queue>#include<math.h>using namespace std;#define maxn 10010int l[maxn],r[maxn],x[maxn*2];int hash[10000100];struct node{int l,r;bool covered;}tree[1000100];void build(int v,int l,int r){tree[v].l=l;tree[v].r=r;tree[v].covered=false;if(l==r) return ;int mid=(tree[v].l+tree[v].r)>>1;int temp=v<<1;build(temp,l,mid);build(temp+1,mid+1,r);}bool query(int v,int l,int r){//如果v区间直接被覆盖的话,那么直接false,因为前面贴的是不可见的 if(tree[v].covered) return false;bool ff;if(tree[v].l==l&&tree[v].r==r){tree[v].covered=true;return true;}int mid=(tree[v].l+tree[v].r)>>1;int temp=v<<1;if(r<=mid) ff=query(temp,l,r);else if(l>mid) ff=query(temp+1,l,r);else{bool f1=query(temp,l,mid);bool f2=query(temp+1,mid+1,r);ff=f1||f2;//只需要部分可见就可以了 }//当左右子节点都被覆盖时,父节点说明也已经是覆盖的了 if(tree[temp].covered&&tree[temp+1].covered){tree[v].covered=true;}return ff;}int main(){int T;scanf("%d",&T);while(T--){int n;scanf("%d",&n);memset(l,0,sizeof(l));memset(r,0,sizeof(r));memset(x,0,sizeof(x));memset(hash,0,sizeof(hash));int count=0;for(int i=1;i<=n;i++){scanf("%d%d",&l[i],&r[i]);x[count++]=l[i];x[count++]=r[i];}sort(x,x+count);count=unique(x,x+count)-x;int no=0;//这里相当于是离散化for(int i=0;i<count;i++){hash[x[i]]=no;if(i<count-1){if(x[i+1]-x[i]==1) no++;else no+=2;}}build(1,0,no);int res=0;for(int i=n;i>=1;i--){if(query(1,hash[l[i]],hash[r[i]])){res++;}}printf("%d\n",res);}}

对于离散化还是需要多多理解与思考!

1 0
原创粉丝点击