题解在注释中,每个函数都有解释。
//
// main.cpp
// Richard
//
// Created by 邵金杰 on 16/7/25.
// Copyright © 2016年 邵金杰. All rights reserved.
//
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
//存放纵轴的
struct line{
double x,y1,y2;
bool bleft;
}line[210];
double y[210];
struct node{
int L,R;
node *pleft,*pright;
int cover;
double len;
}tree[900];
int nNodetree=0;
bool operator < (const struct line &l1,const struct line &l2)
{
return l1.x<l2.x;
}
template<class F,class T>
F bin_search(F s,F e,T val)
{
F L=s;
F R=e-1;
while(L<=R)
{
F mid=L+(R-L)/2;
if(!((*mid>val)||(*mid<val))) return mid;
else if(*mid>val) R=mid-1;
else L=mid+1;
}
return e;
}
int mid(node *p)
{
return (p->L+p->R)/2;
}
//建树
void Buildtree(node *p,int L,int R)
{
p->L=L;
p->R=R;
p->len=0;
p->cover=0;
if(L==R) return ;
nNodetree++;
p->pleft=tree+nNodetree;
nNodetree++;
p->pright=tree+nNodetree;
Buildtree(p->pleft,L,mid(p));
Buildtree(p->pright,mid(p)+1,R);
}
//如果区间刚好在所插入范围内,那么直接把p->len=y[R+1]-y[L],为什么要R+1,因为L和R都代表区间,所以这个区间的最上面的坐标为y[L],最下面的轴是y[R+1],相减就是矩形在该区间内所占的高,前提是完全覆盖该区间的,所以p->cover++,接下来就是区间没有完美配对的情况了,那么区间会分解,这都是套路不说了,最后那句话是更新母节点的(并非是根结点),如果该结点所代表的区间不是完全被覆盖的,那么所覆盖的矩形的高就是p->len=p->pleft->len+p->pright->len;(这句话非常关键,否则根结点无法及时更新的话,会导致该条纵轴与下一条纵轴之间的矩形面积为0,导致最后答案错误);
void Insert(node *p,int L,int R)
{
if(p->L==L&&p->R==R)
{
p->len=y[R+1]-y[L];
p->cover++;
return ;
}
if(R<=mid(p)) Insert(p->pleft,L,R);
else if(L>mid(p)) Insert(p->pright,L,R);
else{
Insert(p->pleft,L,mid(p));
Insert(p->pright,mid(p)+1,R);
}
if(p->cover==0)
p->len=p->pleft->len+p->pright->len;
}
//与Insert类似,如果完美配对,那么矩形之前是完美覆盖该区间的,那么现在矩形到右边的轴了,就不再覆盖了,所以p->cover--,当p->cover==0时,代表该区间已经没有完全覆盖的矩形了,如果时单个区间,那么p->len=0,如果是多个单区间组合起来的组合区间,那么p->len=p->pleft->len+p->pright->len;(因为可能有不完美覆盖该区间的矩形,但是有一部分在该区间内),接下来是区间不配对时的分解区间,最后一个if的意思也是跟新母节点,知道跟新根结点,理由在Insert函数中已经阐述过了,类似。
void Delete(node *p,int L,int R)
{
if(p->L==L&&p->R==R)
{
p->cover--;
if(p->cover==0){
if(p->L==p->R) p->len=0;
else p->len=p->pleft->len+p->pright->len;
}
return ;
}
if(R<=mid(p)) Delete(p->pleft,L,R);
else if(L>mid(p)) Delete(p->pright,L,R);
else{
Delete(p->pleft,L,mid(p));
Delete(p->pright,mid(p)+1,R);
}
if(p->cover==0)
p->len=p->pleft->len+p->pright->len;
}
int main()
{
int n,kase=0,yc,lc;
double x1,x2,y1,y2;
double area;
while(scanf("%d",&n)&&n)
{
area=0;
yc=0,lc=0;
for(int i=0;i<n;i++)
{
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
y[yc++]=y1;
y[yc++]=y2;
line[lc].x=x1;
line[lc].y1=y1;
line[lc].y2=y2;
line[lc].bleft=true;
lc++;
line[lc].x=x2;
line[lc].y1=y1;
line[lc].y2=y2;
line[lc].bleft=false;
lc++;
}
sort(y,y+yc);//整理横轴,然后分解区间
yc=(int)(unique(y,y+yc)-y);//一共有yc条横轴
Buildtree(tree,0,yc-1-1);//yc-1是区间数,因为区间开始是0,所以结束是yc-1-1;
sort(line,line+lc);
for(int i=0;i<lc-1;i++)
{
int L=(int)(bin_search(y,y+yc,line[i].y1)-y);
int R=(int)(bin_search(y,y+yc,line[i].y2)-y);//寻找该矩形的区间
if(line[i].bleft)
Insert(tree,L,R-1);//因为找到的是线的位置,但是插入是对区间操作的,所以区间是从L~R-1的
else
Delete(tree,L,R-1);
area+=tree[0].len*(line[i+1].x-line[i].x);//tree[0]代表的是在line[i].x与line[i+1].x之间举行的宽是多少,相乘之后就代表了在line[i].x~line[i+1].x之间的矩形面积,因为有lc-1个范围,所以每次area都要更新。
}
printf("Test case #%d\n",++kase);
printf("Total explored area: %.2f \n",area);
printf("\n");
}
return 0;
}