//根据几篇的代码修改了,自己好看懂点,haha
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
using namespace std;
struct node{
int l,r,cov;
double len;//cov是覆盖情况,len表示覆盖的长度
}e[222*4];
struct line{//平行于y轴的线段
double x,up,down;
int flag;//1表示矩阵左边,-1是右边
}f[222];
int cmp(line a,line b)
{
return a.x<b.x;
}
double y[222];
void build(int a,int b,int c)//建线段树
{
e[c].l=a;
e[c].r=b;
e[c].len=0;
e[c].cov=0;
if(a==b)
return;
int mid=(a+b)/2;
build(a,mid,2*c);
build(mid+1,b,2*c+1);
}
void update(int a,int b,int c,int flag)//修改覆盖情况cov,以及计算,
{
if(a==e[c].l&&b==e[c].r)
{
if(flag==1)//cov不可能出现负数,因而可以直接判断
{
e[c].cov++;
e[c].len=y[b+1]-y[a];
}
else
{
e[c].cov--;
if(e[c].cov==0)
{
if(e[c].l==e[c].r)//叶结点
e[c].len=0;
else
e[c].len=e[2*c].len+e[2*c+1].len;
}
}
return;
}
int mid=(e[c].l+e[c].r)/2;
if(b<=mid)
update(a,b,2*c,flag);
else if(a>mid)
update(a,b,2*c+1,flag);
else
{
update(a,mid,2*c,flag);
update(mid+1,b,2*c+1,flag);
}
if(e[c].cov==0)
e[c].len=e[2*c].len+e[2*c+1].len;
}
int binary(double a[],int l,int r,double x) //二分查找x,并返回为序号 O(logN)
{
int mid;
while(l<=r)
{
mid = (l+r)>>1;
if(x < a[mid])
r = mid -1;
else
{
if(x > a[mid])
l = mid +1;
else
return mid;
}
}
}
int main()
{
int n,tt=0;
while(scanf("%d",&n)!=EOF)
{
if(n==0)
break;
int i,j,k,t=0;
double x1,y1,x2,y2;
for(i=0;i<n;i++)
{
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
y[t]=y1;f[t].x=x1;f[t].up=y2;f[t].down=y1;f[t++].flag=1;
y[t]=y2;f[t].x=x2;f[t].up=y2;f[t].down=y1;f[t++].flag=-1;
}
sort(y,y+t);//将y1和y2的值离散化成0到t-1的值
sort(f,f+t,cmp);//用结构体数组f记录每条平行于y轴的线段
build(0,t-1,1);
double ans=0;
for(i=0;i<t;i++)
{
if(i!=0)
ans+=(f[i].x-f[i-1].x)*e[1].len;
int l=binary(y,0,t-1,f[i].down);//二分查找离散化对应值
int r=binary(y,0,t-1,f[i].up)-1;//线段长度,由于线段树处理的是点,所以每点表示y[i+1]-y[i]段,所以线段最后点要-1
update(l,r,1,f[i].flag);
}
printf("Test case #%d\nTotal explored area: %.2lf\n\n",++tt,ans);
}
return 0;
}
/*
举例:
数据
2
10 10 20 20
15 15 25 25.5
0
无限延长所有平行于Y轴的线,可得到总面积就是三个矩形((10,10),(15,20)),((15,10)(20,25.5)),((20,15),(25,25.5))面积之和
离散化各点的纵坐标并排序,得到y[0]=10,y[1]=15,y[2]=20,y[3]=25.5;
构建0到3的线段树,其中cov是覆盖情况。从横坐标小的开始枚举所有平行于y轴的线段,如果是矩形左边就cov+1,否则cov-1
这样我们可以知道cov不为0的就是上述三个矩形的宽,长就是两个两个横坐标的差
*/
/*
转载原理:
【题意】:
线段树经典应用,求矩形并的面积
【分析】:
第一道矩形几何题,主要是练习线段树来的
求矩形的并,由于矩形的位置可以多变,因此矩形的面积一下子不好求
这个时候,可以采用“分割”的思想,即把整块的矩形面积分割成几个小矩形的面积,然后求和就行了
这里我们可以这样做,把每个矩形投影到 y 坐标轴上来
然后我们可以枚举矩形的 x 坐标,然后检测当前相邻 x 坐标上 y 方向的合法长度,两种相乘就是面积
然后关键就是如何用线段树来维护那个 “合法长度”
可以这样来搞
线段树的节点这样定义
struct node { int left,right,cov; double len; }
cov 表示当前节点区间是否被覆盖,len 是当前区间的合法长度
然后我们通过“扫描线”的方法来进行扫描
枚举 x 的竖边,矩形的左边那条竖边就是入边,右边那条就是出边了
然后把所有这些竖边按照 x 坐标递增排序,每次进行插入操作
由于坐标不一定为整数,因此需要进行离散化处理
每次插入时如果当前区间被完全覆盖,那么就要对 cov 域进行更新
入边 +1 出边 -1
更新完毕后判断当前节点的 cov 域是否大于 0
如果大于 0,那么当前节点的 len 域就是节点所覆盖的区间
否则
如果是叶子节点,则 len=0
如果内部节点,则 len=左右儿子的 len 之和
转自---------http://blog.sina.com.cn/s/blog_88705ca20100ufl0.html
*/