poj_2528Mayor's posters(线段树)
标签: 线段树
题目连接
Mayor's posters
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 57848 Accepted: 16730
Description
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 li 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 <= li <= ri <= 10000000. After the i-th poster is placed, it entirely covers all wall segments numbered li, li+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
Source
Alberta Collegiate Programming Contest 2003.10.18
- 题意:选区有很多的竞选者,他们要在一面墙上贴大字报,后来的人可以覆盖前面的人贴的大字报,每次会给出大字报的其实位置和终止位置(注意,这里的编号是d对于每个单位线段,而不是对点,注意仔细看图)问最后一个人贴完大字报,墙面上一共可以看到几张大字报,注意,能看到一部分也算可以看到。
- 题解:这个是线段树一个典型的应用,推荐一个很好地ppt,百度搜索浙江大学ppt线段树的讲解就很好。这里线段树中的一个域保存,当前节点的颜色值,这个题可以理解成给一面墙染不同的颜色。如果当前节点全部被同颜色覆盖,那么更新这个点的域为此颜色的编号。如果这个点没有染色,或者有多重颜色的话就更新这个点的域值为-1.
因为这个是我的打的第一个线段树,所以我来讲解一下初步理解的线段树的知识和用法
线段树是一个二叉查找树
线段树一般解决的问题是有关区间的动态修改和查询的问题。
首先要明确的是二叉查找树的性质,有点类似于二分查找的过程,可以仔细理解一下,下面说一下建立一棵树可以直接用数组来保存每个点,因为二叉树的性质,对于每个点来说,他的左孩子的编号是当前节点编号的二倍,表示为:(rt<<2),右孩子编号为其左孩子编号+1,表示为:(rt<<1|1)
那么在更新操作的时候有两种写法,两种理解,第二种不是很好理解,但是写法简单,且不容易出错。下面介绍一下这两种写法:
但是注意无论是哪种写法,建图的时候都是将中间节点归到左孩子。
A.需要截断区间
- 第一种理解是每次都将区间截取,考虑,如果所查询的区间刚好覆盖当前节点的话就将这个点的域(col[rt])直接更新;
- 取当前节点的中间点(m = (l+r)>>1)
- 如果要查询的区间的右端点(R)满足(R<=m) 则只用更新左孩子
如果要查询的区间的左端点(L)满足(L>m) 则只用更新右孩子 - 如果上面都不满足,需要截取区间,更新左孩子,此时查找区间为(L)~(m)
然后再跟新右孩子,此时的查找区间为(m+1)~(R)
其对应的代码如下
Update(int L, int R, int c, int l, int r, int rt)//L,R是要查询的区间,l,r是当前点的区间
{
if(L==l&&R==r){
col[rt] = c;
return;
}
int m = (l+r)>>1;
if(R<=m) Update(L,R,c,l,m,rt<<1);
else if(L>m) Update(L,R,c,m+1,r,rt<<1|1);
else {
Update(L,m,c,l,m,rt<<1);
Update(m+1,R,c,m+1,r,rt<<1|1);
}
}
B.不用截断区间
-
如果要询问的区间覆盖这个区间,即(L<=l&&R>=r)则对于这个点是一定要整个点更新的,所以直接更新并返回就可以了
-
取当前节点的中间点(m = (l+r)>>1)
-
如果当前要询问的点的右端点超过了中间节点的话即(R>m),说明右边的节点有需要更新的点,所以要将右孩子更新。
-
如果当前要询问的点的左端点超过了中间节点的话即(L<=m),说明左边的节点有需要更新的点,所以要将左孩子更新。
这样代码比较简单,如下:
void Update(int L, int R, int l, int r, int c, int rt)
{
if( L <= l && R >= r )
{
col[rt] = c;
return;
}
if(col[rt]!=-1) PushDown(rt);
int m = (l+r)>>1;
if(L <= m ) Update(L,R,l,m,c,rt<<1);
if(R > m) Update(L,R,m+1,r,c,rt<<1|1);
}
查询的时候同样的方法有两种理解。
这里介绍线段树一种很重要的思想,就是懒操作的思想,就是在处理到某个节点的时候指示标记这个节点的状态,而不继续把影响下推下去,只有当用到这个点的时候或者需要下推的时候再将影响下推下去。
对于这个题的下推代码和查询代码主要的思想和核心弄懂了以后就不再赘述,直接上代码。一定要注意的是这个题的数据十分大,如果要想开的下这个数组,所以要先离散化一下。下面介绍一下什么是离散化。
- 离散化:
对于数字的离散化离散化可以想到,是将这些数字排序,对应的下标就是离散化结果,所以在找对应的下标的时候都要用到的是二分查找。但是这个题要注意一个问题,就是考虑110,14,6~10这三个大字报来说如果按照常规的离散化的思路来说的话,会出现问题,就是中间的空隙值没有离散化,为了避免这样的情况,我们每次讲这个点的右端点加1的值也加入这个数组,一起里散话,既可以避免这个问题了。
下面是这个题ac代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
#define M 10005
int m,li[M],ri[M];
int xx[M<<3];
int x[M<<3],col[M<<4],ans;
bool _hash[M];
void PushDown(int rt){//懒操作?
col[rt<<1] = col[rt<<1|1] = col[rt];
col[rt] = -1;
}
void Update(int L, int R, int c, int l, int r, int rt){
if(l>=L&&r<=R){ //L和R是要查询的区间,要查询的区间包含当前节点区间
col[rt] = c;
return;
}
if(col[rt]!=-1) PushDown(rt);
int m = (l+r)>>1;
if(m >= L) Update(L,R,c,l,m,rt<<1);//如果中点左边仍有要查询的点则要查询左结点
if(m < R) Update(L,R,c,m+1,r,rt<<1|1);//如果中间节点右边仍有要查询的点要搜索其右子树
//这里要注意的是大于和小于的区别,因为建图的时候是左边包含中间节点
}
void query(int l, int r, int rt){//查询不是很懂,这里它为什么要遍历整棵书呢?
if(~col[rt] || l == r){
if(~col[rt] && !_hash[col[rt]]){
ans++;
_hash[col[rt]] = true;//这个是干啥的?看上去这个像是对某种颜色是否出现过hash
}
return;
}
// if(col[rt] != -1) PushDown(rt);
int m = (l+r)>>1;
query(l,m,rt<<1);
query(m+1,r,rt<<1|1);
}
int BSearch(int ll,int hh,int xx){
int mm;
while(ll<=hh){
mm = (ll+hh)>>1;
if(x[mm]==xx) return mm;
else if(x[mm]>xx) hh = mm-1;
else ll = mm+1;
}
return -1;
}
int main()
{
int t,n,i;
scanf("%d",&t);
while(t--){
memset(col,-1,sizeof(col));
memset(_hash,0,sizeof(_hash));
int nn = 0;
scanf("%d",&n);
for(i = 1; i <= n; i++){
scanf("%d%d",&li[i],&ri[i]);
x[++nn] = li[i];
x[++nn] = ri[i];
x[++nn] = ri[i]+1;
}
sort(x+1,x+nn+1);
m = unique(x+1, x+nn+1) - x - 1;
for(i = 1; i <= n; i++){
int l = BSearch(1,m,li[i]);
int r = BSearch(1,m,ri[i]);
//printf("(%d %d)
", l, r);
Update(l,r,i,1,m,1);
}
ans = 0;
query(1,m,1);
//for(int i = 1; i <= m; i++)printf("%d ", _hash[i]);puts("");
printf("%d
",ans);
}
return 0;
}
下面是我又敲了一遍的。。一起码上吧
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int N = 10005;
#define dd puts("haha");
int li[N],ri[N];
int x[N<<3];
int col[N<<4];
bool Hash[N];
void PushDown(int rt){
col[rt<<1] = col[rt];
col[rt<<1|1] = col[rt];
col[rt] = -1;
return;
}
void Update(int L, int R, int l, int r, int c, int rt)
{
if( L <= l && R >= r ) {
col[rt] = c;
return;
}
if(col[rt]!=-1) PushDown(rt);
int m = (l+r)>>1;
if(L <= m ) Update(L,R,l,m,c,rt<<1);
if(R > m) Update(L,R,m+1,r,c,rt<<1|1);
}
int ans;
void query(int l, int r, int rt)
{
if(l==r||~col[rt]){//~col[rt]相当于col[rt]!=-1;因为~是按位取反的意思
if(!Hash[col[rt]]&&(col[rt]!=-1)){
ans++;
Hash[col[rt]] = 1;
}
return;
}
if(~col[rt])PushDown(rt);
int m = (l+r)>>1;
query(l,m,rt<<1);
query(m+1,r,rt<<1|1);
}
int BSearch(int l, int r, int c)
{
int m;
while(l<=r){
m =(l+r)>>1;
if(x[m]==c) return m;
else if(x[m]<c) l = m+1;
else if(x[m]>c) r = m-1;
}
return -1;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
memset(col,-1,sizeof(col));
memset(Hash,0,sizeof(Hash));
int n;
scanf("%d",&n);
int cnt = 1;
for(int i = 1; i <=n; i++)
{
scanf("%d %d",&li[i],&ri[i]);
x[cnt++] = li[i];
x[cnt++] = ri[i];
x[cnt++] = ri[i]+1;
}
sort(x+1,x+cnt+1);
int m = unique(x+1,x+cnt+1)-x-1;
for(int i = 1; i <= n; i++){
int ll = BSearch(1,m,li[i]);
int rr = BSearch(1,m,ri[i]);
//printf("(%d %d)
",ll,rr);
Update(ll,rr,1,m,i,1);
}
//dd;
ans = 0;
query(1,m,1);
printf("%d
",ans);
}
return 0;
}