@lunar
2016-07-06T14:25:40.000000Z
字数 2670
阅读 1321
HiHo
这天小Hi和小Ho所在的学校举办社团文化节,各大社团都在宣传栏上贴起了海报,但是贴来贴去,有些海报就会被其他社团的海报所遮挡住。看到这个场景,小Hi便产生了这样的一个疑问——最后到底能有几张海报还能被看见呢?
于是小Ho肩负起了解决这个问题的责任:因为宣传栏和海报的高度都是一样的,所以宣传栏可以被视作长度为L的一段区间,且有N张海报按照顺序依次贴在了宣传栏上,其中第i张海报贴住的范围可以用一段区间[a_i, b_i]表示,其中a_i, b_i均为属于[0, L]的整数,而一张海报能被看到当且仅当存在长度大于0的一部分没有被后来贴的海报所遮挡住。那么问题就来了:究竟有几张海报能被看到呢?
每个测试点(输入文件)有且仅有一组测试数据。
每组测试数据的第1行为两个整数N和L,分别表示总共贴上的海报数量和宣传栏的宽度。
每组测试数据的第2-N+1行,按照贴上去的先后顺序,每行描述一张海报,其中第i+1行为两个整数a_i, b_i,表示第i张海报所贴的区间为[a_i, b_i]。
对于100%的数据,满足N<=10^5,L <= 10^9,0<=a_i < b_i < =L。
对于每组测试数据,输出一个整数Ans,表示总共有多少张海报能被看到。
5 10
4 10
0 2
1 6
5 9
3 4
5
这道题的基础算法是线段树,加上一点离散化的思想。
怎么应用线段树呢?我们这样来做,对于每个节点i,tree[i]
存储当前结点表示区间的最上层海报,如果该区间内只有一张海报那么置为该海报的编号,若有多张海报,那么置为-1,若无海报置为0。在计算能被看到的海报个数时,对线段树遍历,对于每个节点,如果它的tree[i]
不为0或-1的话说明该区间内最上层只有一张海报,检查其海报编号,若集合中没有那就加进去计数,如果tree[i]
为-1,那就递归其子树,直到叶子节点。叶子结点表示区间长度为1,最上层最多就一张海报,所以如果tree[i]
不为0的话那就将其海报编号加进去。建树的过程就是初始化,然后对每个海报信息依次进行线段树的更新,这里更新可以用到上一次提到的懒惰标号传递。
然而这样并不能解决所有问题,我们注意到,O(L)显然是不可接受的,但是仔细思考发现这个L似乎没什么用。我们需要的是各个海报之间的相互关系,也就是说若有两张海报那么{A=[1,100000],B=[2,1000005]}和{A=[1,3],B=[2,4]}是一样的。
这里就可以用到离散化的方法,我们将所有海报的端点信息排序去重,建立起原端点和新端点的双射关系。因为每个海报两个端点,所以最多有2*N个点,也就是说线段树的最多有 (4 * N)个节点,而,这样O(N)的复杂度就可以接受了。
#include<iostream>
#include<map>
#include<algorithm>
#include<set>
#include"math.h">
using namespace std;
#define MAX 100005
#define lchild p<<1
#define rchild p<<1|1
int tree[2*MAX];
int flag[2*MAX];
int s[2*MAX];
int a[MAX];
int b[MAX];
// Map the real coordinate and discrect coordinate
std::map<int,int> distrete;
//Set used to count the amount of post alive finally
std::set<int> v;
void buildTree(int l,int r,int p){
if(l+1==r){
tree[p] = 0;
flag[p] = -1;
return ;
}
tree[p] = 0;
flag[p] = -1;
int m = round((l+r)/2.0);
buildTree(l,m,lchild);
buildTree(m,r,rchild);
}
void update(int l, int r, int L, int R,int v, int p){
if(l<=L&&r>=R){
tree[p] = v;
flag[p] = 1;
return ;
}
if(flag[p] == 1){
tree[lchild] = tree[p];
tree[rchild] = tree[p];
flag[lchild] = 1;
flag[rchild] = 1;
flag[p] = -1;
}
int M = round((L+R)/2.0);
if(l<M) update(l,r,L,M,v,lchild);
if(r>M) update(l,r,M,R,v,rchild);
if((tree[lchild] == tree[rchild]) && (tree[lchild]!=-1)) tree[p] = tree[lchild];
else tree[p] = -1;
}
void stick(int n,int tn){
for(int i = 1;i<=n;i++){
int ta,tb;
std::map<int,int>::iterator it;
it = distrete.find(a[i]);
ta = it->second;
it = distrete.find(b[i]);
tb = it->second;
update(ta,tb,1,tn,i,1);
}
}
void cnt(int l,int r, int p){
if((l+1==r) || (tree[p]!=-1)){
if(tree[p]!=0)v.insert(tree[p]);
}else{
int m = round((l+r));
cnt(l,m,lchild);
cnt(m,r,rchild);
}
}
int main(){
int n,l,ind=1,tn;
cin >> n >> l;
for(int i=1;i<=n;i++){
cin >> a[i] >>b[i];
s[ind++] = a[i];
s[ind++] = b[i];
}
tn = 2*n+1;
//Sort the coordinate
sort(s+1,s+tn);
//Remove the duplication
tn = unique(s+1,s+tn) - (s+1);
for(int i = 1;i<=tn;i++){
distrete[s[i]] = i;
}
//Build segment tree
buildTree(1,tn,1);
//Stick the post
stick(n,tn);
cnt(1,tn,1);
cout << v.size();
return 0;
}