Mayor's posters POJ

The citizens of Bytetown, AB, could not stand that the candidates in the mayoral election campaign have been placing their electoral posters at all places at their whim. The city council has finally decided to build an electoral wall for placing the posters and introduce the following rules: 
  • 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

The first line of input contains a number c giving the number of cases that follow. The first line of data for a single case contains number 1 <= n <= 10000. The subsequent n lines describe the posters in the order in which they were placed. The i-th line among the n lines contains two integer numbers l i and ri which are the number of the wall segment occupied by the left end and the right end of the i-th poster, respectively. We know that for each 1 <= i <= n, 1 <= l i <= ri <= 10000000. After the i-th poster is placed, it entirely covers all wall segments numbered l i, l i+1 ,... , ri.

Output

For each input data set print the number of visible posters after all the posters are placed.

The picture below illustrates the case of the sample input. 

Sample Input

1
5
1 4
2 6
8 10
3 4
7 10

Sample Output

4

题意:有一块长度为10000000的画板,按照顺序给画板上色,最多上色100000次,不考虑画板的高,输入先给出测试样例数,然后是上色的次数再然后是每次上色的区间,表示再[a,b]之间上色,每次的颜色都不一样,问最后在画版上能看
到多少种颜色。

思路:由于题目数据太大,可以考虑相对数据进行离散化,然后利用线段树的思想,按照从后到前的顺序,利用区间修改进行上色,根据区间求最小值判断是否有空白位置可以上色,最后得出结果即可。
具体思路:先将输入的数据存放再两个数组中,再开一个容器将所有的数据存放,然后进行离散化。
离散化是指将所有的数据进行排序,然后去除重复的元素,剩下的就是总数据中不重复的数据,并且这些数据都有一个对应的位置。
然后再开两个数组,根据去重后容器中的数据,将初始的两个数组中的元素转化为它在容器中的位置并存放到新开的数组中,这样离散化就完成了。
最后再按照从后向前的顺序给画板上色,按照线段树的方式,初始值为0表示空白,每次对要上色的区间进行最小值查询:
如果最小值为0,则表示可以上色。最后上色的次数即可。
注意:这个题的数据是比较水的,比如按照上面的思想写出的代码是能够ac的,但是对于1 3 1 10 1 4 6 10这样一组数据,正常情况下应该是3块,
但是代码给出的是2块,这是因为在离散化的时候5消失了,区间2,3并没有存在,对此应该有的改进方法是
再对容器去重后,对容器终点所有数据进行检查,如果前后的数据差值大于1,应当在这两个数据中间插入一个数。
改变后的代码同样能够ac。这里我给出的是改变后的代码但我将改变的那部分代码写成了一个块,如果有兴趣的朋友可以试试去点这个块后的代码同样ac

代码:
  1 #include <cstdio>
  2 #include <fstream>
  3 #include <algorithm>
  4 #include <cmath>
  5 #include <deque>
  6 #include <vector>
  7 #include <queue>
  8 #include <string>
  9 #include <cstring>
 10 #include <map>
 11 #include <stack>
 12 #include <set>
 13 #include <sstream>
 14 #include <iostream>
 15 #define mod 998244353
 16 #define eps 1e-6
 17 #define ll long long
 18 #define INF 0x3f3f3f3f
 19 using namespace std;
 20 
 21 const int maxn = 100000;
 22 //存放输入的数据
 23 int lid[maxn],rig[maxn];
 24 //存放离散化后的数据
 25 int led[maxn],reg[maxn];
 26 //要离散化的数据
 27 vector<int> ve;
 28 
 29 struct node
 30 {
 31     //l表示左边,r表示右边,sum表示该线段的值
 32     int l,r,sum;
 33     //lazy为标记,为方便区间修改
 34     int lazy;
 35 };
 36 node no[maxn*2];
 37 
 38 //初始化
 39 //k表示当前节点的编号,l表示当前区间的左边界,r表示当前区间的右边界
 40 void build(int k,int l,int r)
 41 {
 42     no[k].l=l;
 43     no[k].r=r;
 44     //如果递归到最低点
 45     if(l==r)
 46     {
 47         no[k].sum=0;
 48         return ;
 49     }
 50     //对半分
 51     int mid=(l+r)/2;
 52     //递归到左线段
 53     build(k*2,l,mid);
 54     //递归到右线段
 55     build(k*2+1,mid+1,r);
 56 }
 57 
 58 //传递标记
 59 void pushdown(int k)
 60 {
 61     //当前节点不是最低层时
 62     if(no[k].l!=no[k].r)
 63     {
 64         //更新子节点的值
 65         no[k*2].sum=no[k*2+1].sum=no[k].lazy;
 66         //更新子节点的标记
 67         no[k*2].lazy=no[k*2+1].lazy=no[k].lazy;
 68     }
 69     //清除当前节点的标记
 70     no[k].lazy=0;
 71 }
 72 
 73 //区间修改
 74 void change(int k,int l,int r,int x)
 75 {
 76     //检查并下传标记
 77     if(no[k].lazy)
 78     {
 79         pushdown(k);
 80     }
 81     //到最底层时更新值与标记
 82     if(no[k].l==l&&no[k].r==r)
 83     {
 84         no[k].sum=x;
 85         no[k].lazy=x;
 86         return ;
 87     }
 88     //取中值
 89     int mid=(no[k].l+no[k].r)/2;
 90     
 91     if(r<=mid)
 92     {
 93         //如果被修改区间完全在左区间
 94         change(k*2,l,r,x);
 95     }
 96     else if(l>mid)
 97     {
 98         //如果被修改区间完全在右区间
 99         change(k*2+1,l,r,x);
100     }
101     else
102     {
103         //如果都不在,就要把修改区间分解成两块,分别往左右区间递归
104         change(k*2,l,mid,x);
105         change(k*2+1,mid+1,r,x);
106     }
107     //更新当前节点的值
108     no[k].sum=min(no[k*2].sum,no[k*2+1].sum);
109 }
110 
111 //查询指定区间内的所有的和
112 //k表示当前节点的编号,l表示当前区间的左边界,r表示当前区间的右边界
113 int query(int k,int l,int r)
114 {
115     //检查并下传标记
116     if(no[k].lazy)
117     {
118         pushdown(k);
119     }
120 
121     //如果当前区间就是询问区间,完全重合,那么显然可以直接返回
122     if(no[k].l==l&&no[k].r==r)
123     {
124         return no[k].sum;
125     }
126 
127     //取中值
128     int mid = (no[k].l+no[k].r)/2;
129     
130     if(r<=mid)
131     {
132         //如果询问区间包含在左子区间中
133         return query(k*2,l,r);
134     }
135     else if(l>mid)
136     {
137         //如果询问区间包含在右子区间中
138         return query(k*2+1,l,r);
139     }
140     else
141     {
142         //如果询问区间跨越两个子区间,返回较小的值
143         return min(query(k*2,l,mid),query(k*2+1,mid+1,r));
144     }
145 }
146 
147 //可有可无的方法,
148 void inser()
149 {
150     //对容器中的数据进行检查,如果容器中有元素差距超过1,应当插入一个数
151     vector<int>::iterator it=ve.begin();
152     while(it!=ve.end()-1)
153     {
154         //当前迭代器指向的数
155         int a=*it;
156         //下一个数
157         it++;
158         int b=*it;
159         if(b-a>1)
160         {
161             //插入数
162             it=ve.insert(it,a+1);
163             //指向下一个迭代器
164             it++;
165         }
166     }
167 }
168 int main()
169 {
170     int m,n;
171     scanf("%d",&m);
172     while(m--)
173     {
174         //每个测试样例都需要初始化素有数据
175         ve.clear();
176         memset(no,0,sizeof(no));
177 
178         scanf("%d",&n);
179         for(int i=0;i<n;i++)
180         {
181             scanf("%d %d",&lid[i],&rig[i]);
182             //将输入的数据放入ve中,以便离散化
183             ve.push_back(lid[i]);
184             ve.push_back(rig[i]);
185         }
186 
187         //排序
188         sort(ve.begin(),ve.end());
189         //unique将重复的元素放到容器后面,erase删除容器中重复的元素
190         //此操作执行后容器中剩下的是没有重复的数据
191         ve.erase(unique(ve.begin(),ve.end()),ve.end());
192 
193         //这个方法加不加对题目不影响
194         inser();
195 
196         //对输入的数据进行离散化
197         //lower_bound(a,b,c)返回在区间[a,b]内值c所在的位置
198         for(int i=0;i<n;i++)
199         {
200             led[i]=lower_bound(ve.begin(),ve.end(),lid[i])-ve.begin()+1;
201         }
202         for(int i=0;i<n;i++)
203         {
204             reg[i]=lower_bound(ve.begin(),ve.end(),rig[i])-ve.begin()+1;
205         }
206         //构建线段树
207         build(1,1,ve.size()+1);
208         int ans=0;
209         //从后往前涂染
210         for(int i=n-1;i>=0;i--)
211         {
212             //如果当前区间内最小值为0,表示当前区间内有空白的地方,
213             //这可以再添加一个颜色,
214             if(query(1,led[i],reg[i])==0)
215             {
216                 change(1,led[i],reg[i],i);
217                 ans++;
218             }
219         }
220         printf("%d
",ans);
221     }
222 }
原文地址:https://www.cnblogs.com/mzchuan/p/11787266.html