刚开始学习扫描线,由于网上资料介绍比较抽象(实际是本人智力水平不是很高),整整看了一个上午才对扫描线有点眉目,然后又花了大半个下午才弄出这道基础题,真是忏愧。另外刚开始学习扫描线,我认为代码写的是不太好的,但是将就着,等以后水平高些回来改。会附上比较详细的注释,毕竟是新手。
/*我这里采用了离散化(对结点增加了两个实际值端点)。
看过几个其他解法,如果不怕数据太强,可以用map<double,int>直接检索线段区间就不用离散化。即map<y坐标,位置>
另外还有先用二分查找先找到线段的区间的,感觉比较麻烦。不过想法都很强ORZ*/
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
#define MAX 205
using namespace std;
struct node{
double x,y1,y2; //一条垂直线段的坐标x,y1,y2
int flag; //代表这条线段是出边(先扫到的矩形一边)还是入边(后扫到的矩形另一边),1代表入边,-1代表出边。
node(){}
node(double a,double b,double c,int d){x=a,y1=b,y2=c,flag=d;} //构造函数比较方便
}line[MAX];
bool cmp(node a,node b){ //后面要对线段根据x排序
return a.x<b.x;
}
struct Node{ //线段树结点定义,L,R左右端点,flag代表该区间重叠次数,叶子结点都代表一个线段
int L,R,flag;
double dd,uu,cnt; //dd,uu看后面赋值即知,cnt代表该结点覆盖线段的长度
void fun(int value){flag+=value;if(flag==0)cnt=0;else cnt=uu-dd;} //重叠次数根据出入边的判断要进行操作
}K[MAX<<2];
double Y[MAX];
void build(int l,int r,int rt){ //建树
K[rt].L=l,K[rt].R=r;
K[rt].dd=Y[l],K[rt].uu=Y[r]; //离散化
K[rt].flag=0,K[rt].cnt=0;
int m=l+r>>1;
if(l!=r-1){
build(l,m,rt<<1); //两个端点才构成一个线段,所以叶子结点的L=R-1
build(m,r,rt<<1|1);
}
return ;
}
void update(node line,int rt){ //更新
if(K[rt].dd==line.y1&&K[rt].uu==line.y2){ //使用了延迟,其实也可以直接到叶子结点再全部更新回来
K[rt].fun(line.flag);
if(K[rt].L!=K[rt].R-1&&K[rt].flag==0){//其实就是说某父节点在获取一个出边时的操作,不能像叶子结点一样CNT=0,而要更新(这里使用了一个性质,一入边必有一相同区间的出边)
K[rt].cnt=K[rt<<1].cnt+K[rt<<1|1].cnt;
}
return;
}
if(line.y1>=K[rt<<1|1].dd)update(line,rt<<1|1); //分区更新
else if(line.y2<=K[rt<<1].uu)update(line,rt<<1);
else
{
node temp=line;
temp.y1=K[rt<<1|1].dd;
update(temp,rt<<1|1);
temp=line;
temp.y2=K[rt<<1].uu;
update(temp,rt<<1);
}
if(K[rt].flag==0) //如果父节点有覆盖则其cnt肯定最大了,不需要更新
K[rt].cnt=K[rt<<1].cnt+K[rt<<1|1].cnt;
}
int main(){
int n;
double x1,y1,x2,y2;
int p=1;
while(~scanf("%d",&n)&&n){
for(int i=1;i<=n;i++){
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
line[2*i-1]=node(x1,y1,y2,1);
Y[2*i-1]=y1;
line[2*i]=node(x2,y1,y2,-1);
Y[2*i]=y2;
}
sort(Y+1,Y+1+2*n);
sort(line+1,line+1+2*n,cmp);
build(1,2*n,1);
double res=0;
for(int i=1;i<=2*n;i++){
if(i!=1)res+=K[1].cnt*(line[i].x-line[i-1].x);
update(line[i],1);
}
printf("Test case #%d\n",p++);
printf("Total explored area: %.2f\n\n",res);
}
}