poj2528 Mayor'sposters
题目链接:http://poj.org/problem?id=2528
题目大意:
在一张墙上张贴海报。每张海报有一个张贴区间。后面放的海报与前面放的海报的关系是:覆盖,不相交,相交。给定海报数目与这些海报分别张贴的区间,问:所有的海报张贴完毕后,可以从墙上看见几张海报。
思路:
乍一看,我们可以使用一个长数组记录海报的信息。比如第一张的区间为[a,b],那么可以将数组的位置[a,b]设为1。若第二张的区间为[c,d],那么将[c,d]设为2...以此类推,在程序的最后,我们只需统计长数组中出现的不同数字的个数即可。算法的复杂度为O(nL+ L) 即O(nL),由于n的取值可达10,000,L的取值可达10,000,000。数组开不了这么大,时间限制又只有1000ms,这是不可能完成的任务。
再一看,采用线段树,离散化处理可以很好地解决这个问题。由于坐标的取值可达10,000,000,然而n的取值仅可达10,000。说明分布在区间中的海报是稀疏的。使用离散化可以将坐标的范围极大地缩小,所开数组的长度至多为O(2n),内存限制没有问题!使用线段树对区间进行操作,复杂度为O(nlog2n)即O(nlogn),时间限制没有问题!接下来要解决的问题主要是:
① 区间离散化
② 更新区间
③ 读取海报标记的问题:
Tips:
我的线段树采用一个dat[]数组存储,dat[i]数组存储的是某节点对应的海报序号。
①区间离散化:
离散化是个很好的技术,可以把稀疏分布在大范围内的区间压缩到小范围内,且不影响区间之间的覆盖关系。我也是在网上找了许多博客,才了解这个技术的。(上课没来哈...)
基本的思想:
对于n个海报对应的n个区间来说,定义一个结构体line存储2n个端点,该结构体中存储2个值:val(端点值)、num(属于第num个海报)。最后对line数组进行升序排序、去重。然后,没错!每一个端点对应的序号就是它们在数组中的顺序。将原来每个区间中的2个端点的值替换成它们对应的序号,这样一来,我们就可以构造出新的n个区间了。而且长数组的范围被压缩到了(0,2n]。
②更新区间
Tips:dat[i] = 0表示,节点i的区间没贴海报,或不只一张海报。dat[i]> 0表示,节点i的区间张贴了海报dat[i]。
void update ( fr , to , L , R , k , po)
使用update函数更新线段树:
1.当张贴区间[fr,to]与当前节点k的区间[L,R]不相交时,return;
2.当[fr,to]恰好覆盖节点k的区间[L,R]时,标记已贴海报为po,return;
3.若该区间已贴海报,则pushdown给2个儿子,标记当前dat[k]= 0, 递归给2个儿子
③读取海报标记的问题:
voidsearch (int k)
从树根往下递归,用ans记录节点张贴海报序号不重复 的个数。输出答案。
算法步骤:
①区间离散化,包括排序
②初始化线段树
③更新区间
④读取可见海报张数
算法复杂度:
①O(n + 2nlog2n + 2n) ,即O(nlogn)
②O(n)
③O(nlogL) 即O(nlog2n),即O(nlogn)
④O(n)
总的复杂度为:O(nlogn)
源程序:
#include <stdio.h>
#include <string.h>
#include <algorithm>
using std::sort;
int n,ans;
const int maxn = 20001;
int POST[maxn][2];
bool vis[maxn];
struct line {
int val;
int num;
}Line[maxn];
int dat[4*maxn];
bool cmp(const line & a , const line & b) {
return a.val < b.val;
}
void init(int cnt) {
int t = cnt;
int k = 1;
while( k < t)
k *= 2;
for(int i = 1 ; i <= 2*k - 1 ; i ++)
dat[i] = 0;
}
void update(int fr , int to , int L , int R , int k , int po) {
if(to < L || fr > R)
return;
else if(fr <= L && to >= R)
dat[k] = po;
else
{
if(dat[k] > 0)
{
dat[2*k] = dat[2*k+1] = dat[k];
dat[k] = 0;
}
int mid = (L+R)>>1;
update(fr,to,L,mid,2*k,po);
update(fr,to,mid+1,R,2*k+1,po);
}
}
void search(int k) {
if(dat[k])
{
if(!vis[dat[k]])
{
vis[dat[k]] = true;
ans++;
}
return ;
}
search(2*k);
search(2*k+1);
}
int main() {
int C;
scanf("%d",&C);
while(C--)
{
// 输入并离散化
scanf("%d",&n);
for(int i = 0 ; i < n ; i ++)
{
scanf("%d%d",&POST[i][0],&POST[i][1]);
Line[2*i].num = -(i+1);
Line[2*i].val = POST[i][0];
Line[2*i+1].num = i+1;
Line[2*i+1].val = POST[i][1];
}
sort(Line,Line+2*n,cmp);
int tmp = Line[0].val, cnt = 1;
for(int i = 0 ; i < 2*n ; i ++)
{
if(Line[i].val != tmp)
{
tmp = Line[i].val;
cnt ++;
}
if(Line[i].num < 0)
POST[-Line[i].num-1][0] = cnt;
else
POST[Line[i].num-1][1] = cnt;
}
init(cnt);
for(int i = 0 ; i < n ; i ++)
update(POST[i][0],POST[i][1],1,cnt,1,i+1);
memset(vis,0,sizeof(vis));
ans = 0;
search(1);
printf("%d\n",ans);
}
return 0;
}