题意:
在一个平面上,依次放入N个颜色不同的矩形(可能会覆盖),现在求最终状态下能看到多少种不同温度颜色。注:空白部分也视为一种颜色。
分析:
这是一道比较有趣的数据结构题:
利用扫描线算法,首先将所有坐标离散化,然后按照正方向枚举x坐标,每次求出当前这个横坐标能看见的矩形,于是将每个矩形视作两条线段:
矩形的左侧视为加入当前矩形,右侧+1的位置视为删去当前矩形。
用线段树+set维护。
对于每个线段树上的节点,存储两个值与一个set,分别设为maxv,minv,S
maxv表示:当前区间内能够看见的,编号最大的未被标记的颜色。
minv表示:当前区间内被覆盖的最小的颜色。(用于更新maxv)
S存储覆盖了当前区间的所有颜色。
对于每次更新,首先求出左右儿子节点的maxv,minv,令当前节点的
,
,理解起来很容易,里层的min就是左右区间内的被覆盖颜色的最小值,而外层的max是因为:当前区间内都被这个值覆盖,所以取max。
然后,若
,说明当前maxv这个颜色一定被其他已更新颜色覆盖掉了,因为它连当前区间内最小的值都不够。所以将
修正为-1,即记为当前区间不含可以被更新的颜色。
每更新一次,继续判断根节点的maxv是否为-1,若不是则继续标记,若是则停止。
实现上有些细节要考虑:首先离散化后,必须区分“离散化后相邻”和“离散化前相邻”的情况(实现时就会发现了)。
并且,由于题目给出的是端点坐标,因此我们的矩形范围要作出相应改动,比如,两个点(0,1)与(0,2)之间,如果直接将这个作为矩形的边界位置(即将左端点设为1,右端点设为2),那么矩形就会有2的单位长度,但其实这个矩形只有1单位长度。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<set>
#define SF scanf
#define PF printf
#define MAXN 100010
using namespace std;
struct node{
set<int> s;
int mi,ma;
node *pl,*pr;
}tree[MAXN*12],*root=tree,*ncnt=tree;
bool used[MAXN];
struct block{
int l,r,id,flag,x;
block() {}
block(int l1,int r1,int id1,int flag1,int x1):l(l1),r(r1),id(id1),flag(flag1),x(x1) {}
bool operator < (const block&a) const{
if(x==a.x)
return l<a.l;
return x<a.x;
}
}blo[MAXN*2];
void build(node *x,int l,int r){
x->mi=-1,x->ma=-1;
if(l==r)
return ;
int mid=(l+r)>>1;
x->pl=++ncnt;
build(x->pl,l,mid);
x->pr=++ncnt;
build(x->pr,mid+1,r);
}
void pushup(node *x){
if(x->pl==NULL&&x->pr==NULL){
if(x->s.empty()){
x->ma=x->mi=-1;
return;
}
x->ma=*x->s.rbegin();
x->mi=x->ma;
if(used[x->ma]==1)
x->ma=-1;
}
else{
int &mi=x->mi,&ma=x->ma;
mi=min(x->pl->mi,x->pr->mi),x->ma=max(x->pl->ma,x->pr->ma);
if(x->s.empty())
return ;
int xs=*x->s.rbegin();
mi=max(mi,xs);
if(ma<xs){
if(used[xs]==1)
ma=-1;
else{
if(xs<mi)
ma=-1;
else
ma=xs;
}
}
}
}
void add(node *x,int l,int r,int l1,int r1,int val,int idx){
if(l>=l1&&r<=r1){
if(val==-1)
x->s.erase(idx);
else
x->s.insert(idx);
pushup(x);
return ;
}
int mid=(l+r)>>1;
if(mid>=l1)
add(x->pl,l,mid,l1,r1,val,idx);
if(mid<r1)
add(x->pr,mid+1,r,l1,r1,val,idx);
pushup(x);
}
void Update(node *x,int l,int r,int l1,int r1,int idx){
if(l>=l1&&r<=r1){
pushup(x);
return ;
}
int mid=(l+r)>>1;
if(mid>=l1)
Update(x->pl,l,mid,l1,r1,idx);
if(mid<r1)
Update(x->pr,mid+1,r,l1,r1,idx);
pushup(x);
}
int n,sumx,sumy;
int apx[MAXN*6],apy[MAXN*6];
int ls[MAXN],rs[MAXN];
void pushx(int x){
apx[++sumx]=x;
apx[++sumx]=x-1;
apx[++sumx]=x+1;
}
void pushy(int y){
apy[++sumy]=y;
apy[++sumy]=y-1;
apy[++sumy]=y+1;
}
int main(){
SF("%d",&n);
int sx,sy,ex,ey,cnt=0;
for(int i=1;i<=n;i++){
SF("%d%d%d%d",&sx,&sy,&ex,&ey);
ex--,ey--;
blo[++cnt]=block(sy,ey,i,1,sx);
blo[++cnt]=block(sy,ey,i,-1,ex+1);
pushx(sx);
pushx(ex+1);
pushy(sy);
pushy(ey);
}
sort(apx+1,apx+1+sumx);
sort(apy+1,apy+1+sumy);
sumx=unique(apx+1,apx+1+sumx)-(apx+1);
sumy=unique(apy+1,apy+1+sumy)-(apy+1);
for(int i=1;i<=cnt;i++){
blo[i].l=lower_bound(apy+1,apy+1+sumy,blo[i].l)-apy;
blo[i].r=lower_bound(apy+1,apy+1+sumy,blo[i].r)-apy;
blo[i].x=lower_bound(apx+1,apx+1+sumx,blo[i].x)-apx;
}
for(int i=1;i<=cnt;i++){
ls[blo[i].id]=blo[i].l;
rs[blo[i].id]=blo[i].r;
}
sort(blo+1,blo+1+cnt);
int las=1;
build(root,1,sumy);
/*for(int i=1;i<=cnt;i++)
PF("{%d %d %d %d %d}\n",blo[i].l,blo[i].r,blo[i].x,blo[i].id,blo[i].flag);*/
for(int i=1;i<=sumx;i++){
while(las<=cnt&&blo[las].x==i){
//PF("{%d %d %d %d}\n",blo[las].l,blo[las].r,blo[las].x,blo[las].id);
add(root,1,sumy,blo[las].l,blo[las].r,blo[las].flag,blo[las].id);
las++;
}
while(root->ma!=-1){
int x=root->ma;
used[x]=1;
Update(root,1,sumy,ls[x],rs[x],x);
}
}
int ans=1;
for(int i=1;i<=n;i++)
if(used[i]==1)
ans++;
PF("%d",ans);
}