题意就是贴海报,然后新贴的海报之间可以覆盖以前贴的,问最后可以看见多少张海报?
海报端点坐标很范围很大,但是对我们来说有用的也就是端点的坐标,例如[1000,2000],[1990,2012]我们用不到[-∞,999][1001,1989][1991,1999][2001,2011][2013,+∞]这些区间,所以我只需要1000,1990,2000,2012就够了,将其分别映射到0,1,2,3,这样子线段树维护的就是0到3这个区间了(将浪费的部分去掉了),而不是0到2012,这样就大大的节省了内存。然后还有这个题的离散化比较特殊,不是普通的离散化还要注意的是这里的每个海报的端点坐标表示的不是一个点是一个单位长度,例如海报有三张分别为1,10 1,4 9,10.离散化后对应的就是1,4 1,2 3,4。原本不离散化是可以看见3种海报的,离散化后就只能看见2种了,这是因为离散化后,原本不相邻的点变相邻了,比如说,未离散化前的4和9,离散化后就变成了2和3,这样子就把未离散化前5到8位置的海报给挤出去了,所以就少数了一种,为了解决这个问题,需要要离散化前原本不相邻的2个点(即距离>=2的2个点)之间插入一个点,相当于给之前举的例子中的5到8占个位子。
#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
#define ls i<<1
#define rs (i<<1)|1
const int maxn=11111;
struct node1
{
int l,r,id;//对叶子节点来说,id就是[l,r]位置的海报,对非叶子节点来说是lazy标记,表示其对应区间的所有点的海报的种类都是id
}segtree[maxn*16+5];
struct node2
{
int l,r;
}poster[maxn+5];
bool vis[10010];vis[i]标记第i种海报有没有数过
int seg[4*maxn+5];//用seg数组完成离散化
int ans;
void pushdown(int i)
{
if(segtree[i].id!=-1)
{
segtree[ls].id=segtree[rs].id=segtree[i].id;
segtree[i].id=-1;
}
}
void build(int i,int l,int r)
{
segtree[i].l=l;
segtree[i].r=r;
segtree[i].id=-1;
if(l==r) return ;
int mid=(l+r)/2;
build(ls,l,mid);
build(rs,mid+1,r);
}
int bsearch(int low,int high,int num)
{
int mid;
while(low<=high)
{
mid=(low+high)>>1;
if(num==seg[mid])
return mid;
else
if(seg[mid]>num)
high=mid-1;
else
low=mid+1;
}
return low;//返回low,high都可以
}
void update(int i,int l,int r,int id)
{
if((l==segtree[i].l)&&(segtree[i].r==r))//条件满足,则可以使用lazy成段更新
{
segtree[i].id=id;
return ;
}
pushdown(i);
int mid=(segtree[i].l+segtree[i].r)>>1;
if(r<=mid)
update(ls,l,r,id);
else
if(l>=mid+1)
update(rs,l,r,id);
else
{
update(ls,l,mid,id);
update(rs,mid+1,r,id);
}
}
void query(int i,int l,int r)
{
if(segtree[i].id!=-1)//不必理会是叶子节点还是非叶子节点,id!=-1就表示其对应区间的海波还是第id钟
{
if(!vis[segtree[i].id])
{
vis[segtree[i].id]=true;
ans++;
}
return ;
}
if(l==r) return ;
pushdown(i);
int mid=(segtree[i].l+segtree[i].r)/2;
if(r<=mid)
query(ls,l,r);
else
if(l>=(mid+1))
query(rs,l,r);
else
{
query(ls,l,mid);
query(rs,mid+1,r);
}
}
int main()
{
int c,n,cnt,m;
scanf("%d",&c);
while(c--)
{
memset(vis,0,sizeof(vis));
cnt=0;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d%d",&poster[i].l,&poster[i].r);
seg[cnt++]=poster[i].l;
seg[cnt++]=poster[i].r;
}
sort(seg,seg+cnt);
int m=unique(seg,seg+cnt)-seg;//m是不重复元素的个数,指向最后一个不重复元素的下一个位置
for(int i=m-1;i>=1;i--)
if((seg[i]-seg[i-1])>1) seg[m++]=seg[i-1]+1;//加数进数组从不重复元素的尾部,m代表不同元素的个数
sort(seg,seg+m);加完之后排个序,离散化就搞定了
build(1,1,m);//实际区间是从0到m-1的,因为我们把poster的端点坐标离散为其对应的数组元素的下标了,但是习惯起见线段树维护的区间就定为1到m,不过也没关系只要后面对线段树的操作都是在1到m范围即可
for(int i=0;i<n;i++)
{
int l=bsearch(0,m-1,poster[i].l);//二分查找在数组seg里找POSTER[I].L的存放位置,即其离散化的值
int r=bsearch(0,m-1,poster[i].r);
update(1,l+1,r+1,i);//因为建树的时候维护的是1,到m,相当于整个区间往右移动一个单位,那么更新区间时也要一致
}
ans=0;
query(1,1,m);//查询区间也要一致
printf("%d
",ans);
}
return 0;
}