题目链接:Hihocoder-1079
主要思路:
由于本题数值范围较大,又只用比较大小关系即可,故用离散化来离散左端点和右端点即可.又因为这道题是连续性线段树,故我们只用将编号便给每条线段.故点1到点2这条线段编为1,点2到点3这条线段编为2,以此类推.故每个点区间[L,R]为线段区间[L,R-1].
用线段树存下区间内海报的状态(如有一张海报则存该海报的编号,若多张或是没有则存-1).最后枚举每一条线段即可.
AC代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define M 100005
using namespace std;
int L[M],R[M],C[M<<1],id=0;
struct node {
int L,R,d,add;
node() {
d=-1;
add=0;
}
void Add(int t){
add=t;
d=t;
return;
}
};
struct Segment {
node tree[M<<2];//四倍数组注意
int tot;
bool mark[M];//存这个海报被枚举过了没
Segment(){
memset(mark,0,sizeof(mark));//初始化
tot=0;
}
void Down(int p){
int &t=tree[p].add;
if(t==0)return;
tree[p<<1].Add(t);
tree[p<<1|1].Add(t);
t=0;
return;
}
void Build(int L,int R,int p) {
tree[p].d=tree[p].add=0;
tree[p].L=L,tree[p].R=R;
if(L==R) {
return;
}
int mid=L+R>>1;
Build(L,mid,p<<1);
Build(mid+1,R,p<<1|1);
}
void turn(int L,int R,int d,int p) {
if(tree[p].L==L&&tree[p].R==R) {
tree[p].d=d;
tree[p].add=d;
return;
}
Down(p);//每次遍历到这个节点时更新他的子节点.(即为延时更新)
int mid=tree[p].L+tree[p].R>>1;
if(R<=mid){
turn(L,R,d,p<<1);
}else if(L>mid)turn(L,R,d,p<<1|1);
else {
turn(L,mid,d,p<<1);
turn(mid+1,R,d,p<<1|1);
}
}
void Query(int p) {
int L=tree[p].L,R=tree[p].R;
if(L==R) {
int ans=tree[p].d;
if(ans<=0)return;//如果没有被覆盖到
if(mark[ans])return;
mark[ans]=1;
tot++;
return;
}
Down(p);
int mid=L+R>>1;
Query(p<<1);//枚举左边那一段
Query(p<<1|1);//枚举右边那一段
}
}S;
int main() {
int n,l;
scanf("%d%d",&n,&l);
for(int i=1; i<=n; i++) {
scanf("%d%d",&L[i],&R[i]);
C[++id]=L[i];
C[++id]=R[i];
}
sort(C+1,C+n+n+1);
id=unique(C+1,C+n+n+1)-C-1;//去重
S.Build(1,id-1,1);
for(int i=1; i<=n; i++) {
L[i]=lower_bound(C+1,C+id+1,L[i])-C;//离散化
R[i]=lower_bound(C+1,C+id+1,R[i])-C;
//printf("%d %d\n",L[i],R[i]-1);
S.turn(L[i],R[i]-1,i,1);//注意是R[i]-1(此处为线段编号,而不是点的编号)
}
S.Query(1);
printf("%d\n",S.tot);
}
如果线段树不懂可以看一下我的线段树模板.