//根据几篇的代码修改了,自己好看懂点,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 */