题目:给定平面上若干矩形,求出被这些矩形覆盖过至少两次的区域的面积.
思路:线段树扫描线。
之前写过求矩形并的面积,求至少覆盖两次的面积只需要对线段树进行小小的修改。
线段树节点定义如下:
<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; }