题目:给定平面上若干矩形,求出被这些矩形覆盖过至少两次的区域的面积.
思路:线段树扫描线。
之前写过求矩形并的面积,求至少覆盖两次的面积只需要对线段树进行小小的修改。
线段树节点定义如下:
<span style="font-size:14px;">struct Node{
int Cover;//覆盖次数
double CoverL[3];//CoverL[i]=覆盖>=i次的长度
};</span>
然后更新节点的过程,以CoverL[2]为例:
如果Cover==0 则CoverL[2]利用子树的CoverL[2]来更新
如果Cover==1 则CoverL[2]利用子树的CoverL[1]来更新
如果Cover==2 则CoverL[2]利用子树的CoverL[0]来更新
其中CoverL[0]就是每个节点的总长度。
代码如下:
//218MS 1816K
#include <iostream>
#include <cstdio>
#include <algorithm>
#define eps 1e-9
#define maxn 2007
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
using namespace std;
int sgn(double x){return (x>-eps)-(x<eps);}
//记录线段
struct Lines{
double x,y1,y2;
bool IN;
Lines(){}
Lines(double x,double y1,double y2,bool IN):x(x),y1(y1),y2(y2),IN(IN){}
bool operator <(const Lines &B)const{return sgn(x-B.x)<0;}
}L[maxn];
//离散化
int n,T,N,Rn;
double Rank[maxn];
int GetRank(double x){
int L=0,R=Rn;//[L,R] first >= x
while(L^R){
int M=(L+R)>>1;
if(Rank[M]<x) L=M+1;
else R=M;
}
return L;
}
//线段树
struct Node{
int Cover;//覆盖次数
double CoverL[3];//CoverL[i]=覆盖>=i次的长度
}K[maxn<<2];
void PushUp(int rt){
int X;
if(K[rt].Cover<=1){
X=1-K[rt].Cover;
K[rt].CoverL[1]=K[rt<<1].CoverL[X]+K[rt<<1|1].CoverL[X];
}
else K[rt].CoverL[1]=K[rt].CoverL[0];
if(K[rt].Cover<=2){
X=2-K[rt].Cover;
K[rt].CoverL[2]=K[rt<<1].CoverL[X]+K[rt<<1|1].CoverL[X];
}
else K[rt].CoverL[2]=K[rt].CoverL[0];
}
void Build(int l,int r,int rt){
if(l==r){
K[rt].Cover=0;
K[rt].CoverL[0]=Rank[l]-Rank[l-1];
K[rt].CoverL[1]=K[rt].CoverL[2]=0.0;
return;
}
int m=(l+r)>>1;
Build(ls);
Build(rs);
K[rt].Cover=0;
K[rt].CoverL[0]=K[rt<<1].CoverL[0]+K[rt<<1|1].CoverL[0];
K[rt].CoverL[1]=K[rt].CoverL[2]=0.0;
}
void Update(int L,int R,int C,int l,int r,int rt){
if(L <= l && r <= R){
K[rt].Cover+=C;
if(l==r){
K[rt].CoverL[1]=K[rt].Cover >= 1 ? K[rt].CoverL[0] : 0.0;
K[rt].CoverL[2]=K[rt].Cover >= 2 ? K[rt].CoverL[0] : 0.0;
}
else PushUp(rt);
return;
}
int m=(l+r)>>1;
if(L <= m) Update(L,R,C,ls);
if(R > m) Update(L,R,C,rs);
PushUp(rt);
}
int main(void)
{
scanf("%d",&T);
while(T--){
scanf("%d",&n);N=n<<1;
for(int i=0;i<n;++i){
double x1,y1,x2,y2;
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
Rank[i<<1]=y1;
Rank[i<<1|1]=y2;
L[i<<1]=Lines(x1,y1,y2,true);
L[i<<1|1]=Lines(x2,y1,y2,false);
}
//线段排序
sort(L,L+N);
//离散化
sort(Rank,Rank+N);Rn=0;
for(int i=1;i<N;++i){
if(sgn(Rank[i]-Rank[i-1])) Rank[++Rn]=Rank[i];
}
//建立线段树
Build(1,Rn,1);
//开始扫描
double X,PreX=L[0].x,PreL2=0.0,AREA2=0.0;
int I=0;
while(I<N){
X=L[I].x;
AREA2+=(L[I].x-PreX)*PreL2;//累计面积
while(I<N&&!sgn(L[I].x-X)){//更新线段树
Update(GetRank(L[I].y1)+1,GetRank(L[I].y2),L[I].IN?1:-1,1,Rn,1);
++I;
}
PreX=X;
PreL2=K[1].CoverL[2];
}
printf("%.2f\n",AREA2);
}
return 0;
}