这道题目让我又重新认识了一下离散化:
首先总结一下离散化的特点:
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 10010 int 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); } }
对于离散化还是需要多多理解与思考!
版权声明:本文为博主原创文章,未经博主允许不得转载。
poj(2528)——Mayor's posters(线段树+离散化)