http://poj.org/problem?id=2528
https://www.luogu.org/problem/UVA10587
Description
- Every candidate can place exactly one poster on the wall.
- All posters are of the same height equal to the height of the wall; the width of a poster can be any integer number of bytes (byte is the unit of length in Bytetown).
- The wall is divided into segments and the width of each segment is one byte.
- Each poster must completely cover a contiguous number of wall segments.
They have built a wall 10000000 bytes long (such that there is enough place for all candidates). When the electoral campaign was restarted, the candidates were placing their posters on the wall and their posters differed widely in width. Moreover, the candidates started placing their posters on wall segments already occupied by other posters. Everyone in Bytetown was curious whose posters will be visible (entirely or in part) on the last day before elections.
Your task is to find the number of visible posters when all the posters are placed given the information about posters' size, their place and order of placement on the electoral wall.
Input
Output
The picture below illustrates the case of the sample input.
![](https://i-blog.csdnimg.cn/blog_migrate/85d8df2191db8df82ba259ebe2bfe93e.jpeg)
Sample Input
1 5 1 4 2 6 8 10 3 4 7 10
Sample Output
4
题目大意:
给你一个无限长的板子,n(n<=10000)个人依次贴n张等高的海报,给出每张海报所贴的范围li,ri(1<=li<=ri<=10000000),求出最后还能看见多少张海报。
题解:
一看线段树没跑了对吧? 就像染色一样,贴一张海报就把那一段染上新颜色, 这就是个很明显的区间修改嘛, 最后统计线段上有多少种不同颜色输出答案就OK了。
但是要注意线段树区间更新问题,给的长度的可能非常大,有1e9,不加处理直接维护一个线段树肯定会MLE,TLE,
但是我们注意到一共最多只有2e4个点,因此我们可以用离散化的思想先对区间进行预处理,所谓的离散化,
在我理解看来就是将一个很大的区间映射为一个很小的区间,而不改变原有的大小覆盖关系,但是注意简单的离散化可能
会出现错误,给出下面两个简单的例子应该能体现普通离散化的缺陷:
例子一:1-10 1-4 5-10
例子二:1-10 1-4 6-10
普通离散化后都变成了[1,4][1,2][3,4]
线段2覆盖了[1,2],线段3覆盖了[3,4],那么线段1是否被完全覆盖掉了呢?
例子一是完全被覆盖掉了,而例子二没有被覆盖
解决的办法则是对于距离大于1的两相邻点,中间再插入一个点。
下面的离散化方法摘自别处(分不清哪位大佬是原创。。。反正不是本蒟蒻):
解法:离散化,如下面的例子(题目的样例),因为单位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。
解这题特意去看了下STL中的unique函数(from:https://www.cnblogs.com/wangkundentisy/p/9033782.html)
unique函数属于STL中比较常用函数,它的功能是元素去重。即”删除”序列中所有相邻的重复元素(只保留一个)。
此处的删除,并不是真的删除,而是指重复元素的位置被不重复的元素给占领了。由于它”删除”的是相邻的重复元素,所以在使用unique函数之前,一般都会将目标序列进行排序。
unique函数的函数原型如下:
只有两个参数,且参数类型都是迭代器:
iterator unique(iterator it_1,iterator it_2);
这种类型的unique函数是我们最常用的形式。其中这两个参数表示对容器中[it_1,it_2)范围的元素进行去重(注:区间是前闭后开,即不包含it_2所指的元素),返回值是一个迭代器,它指向的是去重后容器中不重复序列的最后一个元素的下一个元素。
unique函数通常和erase函数一起使用,来达到删除重复元素的目的。(注:此处的删除是真正的删除,即从容器中去除重复的元素,容器的长度也发生了变换;而单纯的使用unique函数的话,容器的长度并没有发生变化,只是元素的位置发生了变化)
1 #include <stdio.h> 2 #include <string.h> 3 #include <iostream> 4 #include <string> 5 #include <math.h> 6 #include <algorithm> 7 #include <vector> 8 #include <queue> 9 #include <set> 10 #include <map> 11 #include <math.h> 12 const int INF=0x3f3f3f3f; 13 typedef long long LL; 14 const int mod=1e9+7; 15 //const double PI=acos(-1); 16 const int maxn=100010; 17 using namespace std; 18 //ios::sync_with_stdio(false); 19 // cin.tie(NULL); 20 21 struct node 22 { 23 int l; 24 int r; 25 int color; 26 }SegTree[30010<<2]; 27 28 struct Segment 29 { 30 int l; 31 int r; 32 }A[10010]; 33 int B[30010]; 34 int vis[30010<<4]; 35 int T,n,tot,cnt,ans; 36 37 int init()//初始化,离散化 38 { 39 scanf("%d",&n); 40 tot=0;//tot为离散化的计数器 41 for(int i=1;i<=n;i++) 42 { 43 scanf("%d %d",&A[i].l,&A[i].r); 44 B[++tot]=A[i].l; 45 B[++tot]=A[i].r; 46 B[++tot]=A[i].r+1;//完善后的离散化多加的一点 47 } 48 sort(B+1,B+1+tot); 49 int len=unique(B+1,B+1+tot)-B-1;//去除重点,求出规格 50 for(int i=1;i<=n;i++)//重新算出每个贴纸的区间 51 { 52 A[i].l=lower_bound(B+1,B+1+len,A[i].l)-B; 53 A[i].r=lower_bound(B+1,B+1+len,A[i].r)-B; 54 } 55 return len; 56 } 57 58 void PushDown(int rt) 59 { 60 if(SegTree[rt].color!=-1) 61 { 62 SegTree[rt<<1].color=SegTree[rt].color; 63 SegTree[rt<<1|1].color=SegTree[rt].color; 64 SegTree[rt].color=-1; 65 } 66 } 67 68 void Build(int l,int r,int rt) 69 { 70 SegTree[rt].l=l; 71 SegTree[rt].r=r; 72 SegTree[rt].color=0; 73 if(l==r) 74 { 75 return; 76 } 77 int mid=(l+r)>>1; 78 Build(l,mid,rt<<1); 79 Build(mid+1,r,rt<<1|1); 80 } 81 82 void Update(int L,int R,int color,int rt) 83 { 84 int l=SegTree[rt].l; 85 int r=SegTree[rt].r; 86 if(L<=l&&R>=r) 87 { 88 SegTree[rt].color=color; 89 return ; 90 } 91 PushDown(rt); 92 int mid=(l+r)>>1; 93 if(L<=mid) 94 Update(L,R,color,rt<<1); 95 if(R>mid) 96 Update(L,R,color,rt<<1|1); 97 } 98 99 void Query(int L,int R,int rt) 100 { 101 if(SegTree[rt].color!=-1) 102 { 103 if(!vis[SegTree[rt].color]) 104 { 105 ans++; 106 vis[SegTree[rt].color]=1; 107 } 108 return; 109 } 110 Query(L,R,rt<<1); 111 Query(L,R,rt<<1|1); 112 } 113 114 int main() 115 { 116 scanf("%d",&T); 117 while(T--) 118 { 119 int len=init(); 120 cnt=0;//区间染色时的计数器 121 Build(1,len,1);//建树 122 for(int i=1;i<=n;i++)//染色 123 { 124 Update(A[i].l,A[i].r,++cnt,1); 125 } 126 memset(vis,0,sizeof(vis)); 127 ans=0; 128 vis[0]=1; 129 Query(1,len,1); 130 printf("%d\n",ans); 131 } 132 return 0; 133 }
据说可以用Chtholly Tree珂朵莉树做,反正我不会。。。
珂朵莉树详解:https://www.luogu.org/blog/ACdreamer/chtholly-tree
该题的ODT做法可在这里找https://www.luogu.org/problemnew/solution/UVA10587