覆盖的面积
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 880 Accepted Submission(s): 394
Problem Description
给定平面上若干矩形, 求出被这些矩形覆盖过至少两次的区域的面积.
Input
输 入数据的第一行是一个正整数T(1<=T<=100), 代表测试数据的数量. 每个测试数据的第一行是一个正整数N(1<=N& lt;=1000), 代表矩形的数量, 然后是N 行数据, 每一行包含四个浮点数, 代表平面上的一个矩形的左上角坐标和右下角坐标, 矩形的上下边和X 轴平行, 左右边和Y 轴平行. 坐标的范围从0 到100000.
注意: 本题的输入数据较多, 推荐使用scanf 读入数据.
Output
对于每组测试数据, 请计算出被这些矩形覆盖过至少两次的区域的面积. 结果保留两位小数.
Sample Input
2
5
1 1 4 2
1 3 3 7
2 1.5 5 4.5
3.5 1.25 7.5 4
6 3 10 7
3
0 0 1 1
1 0 2 1
2 0 3 1
Sample Output
7.63
0.00
离散化 + 线段树 .
读入数据时记录竖着的边的两端坐标 x,y1,y2 和 Y 轴上出现过的坐标 tempy[], 对 tempy 从小到大排序 , 去掉重复的 y 轴坐标转存到数组 y[], 利用 y[] 的数据对 Y 轴进行离散化建立线段树 , 然后对竖着的边排序 , 从左到右扫描 , 当扫描到一个矩形的左竖边时 , 该线段对应的区间的覆盖次数加一 , 如果是右竖边的话 , 覆盖次数减一 , 若同一段线段被覆盖两次以上 , 则需要对这一段线段进行面积的计算 , 即线段覆盖的长度 * 相邻两竖边之间的距离 , 累加到最后就是结果了 .
代码如下 :
#include<stdio.h> #include<string.h> #include<algorithm> #include<cmath> using std::sort; #define MAXN 1005 #define eps 1e-8 struct Line { double x,y1,y2; int flag; bool operator < (Line const &a) const { if (fabs(x-a.x)<=eps) return flag<a.flag; //当有左右竖边重叠的时候,右边优先扫描 return x<a.x; } } line[2*MAXN]; struct ST { int l,r; //线段树对应的数组y[]的下标 int lc,rc; int covn; //该线段被覆盖次数 double covlen; //该线段被覆盖两次或以上的长度 } st[10*MAXN]; double y[2*MAXN],tempy[2*MAXN]; void build(int l,int r,int n) { st[n].l=l; st[n].r=r; st[n].covlen=0; st[n].covn=0; st[2*n].covlen=0; st[2*n].covn=0; st[2*n+1].covlen=0; st[2*n+1].covn=0; if (r-l==1) return ; int mid=(l+r)/2; build(l,mid,2*n); build(mid,r,2*n+1); } void update(int n,Line line) { if (fabs(line.y1-y[st[n].l])<=eps&&fabs(line.y2-y[st[n].r])<=eps) st[n].covn+=line.flag; if (st[n].r-st[n].l>1) { if (line.y1>y[st[2*n+1].l]||fabs(line.y1-y[st[2*n+1].l])<=eps) update(2*n+1,line); else if (line.y2<y[st[2*n].r]||fabs(line.y2-y[st[2*n].r])<=eps) update(2*n,line); else { Line l1=line,l2=line; l1.y2=y[st[2*n].r]; l2.y1=y[st[2*n+1].l]; update(2*n,l1); update(2*n+1,l2); } } if (st[n].covn>1) st[n].covlen=y[st[n].r]-y[st[n].l]; else st[n].covlen=st[2*n].covlen+st[2*n+1].covlen; } int main () { int cas,t,xs,i,n; double x1,y1,x2,y2; scanf ("%d",&cas); while (cas--) { xs=1; scanf("%d",&n); for (i=1; i<=n; ++i) { scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2); line[xs].x=x1; line[xs].y1=y1; line[xs].y2=y2; line[xs].flag=1; xs++; line[xs].x=x2; line[xs].y1=y1; line[xs].y2=y2; line[xs].flag=-1; tempy[xs-1]=y1; tempy[xs]=y2; ++xs; } sort(line+1,line+xs); sort(tempy+1,tempy+xs); t=0; y[0]=-21000000; xs--; for (i=1; i<=xs; ++i) { if (fabs(tempy[i]-y[t])<=eps) continue; //除去相同的y坐标 y[++t]=tempy[i]; } build(1,t,1); double res=0; for (i=1; i<xs; ++i) //从左到右扫描竖边 { if (i!=1) res+=(line[i].x-line[i-1].x)*st[1].covlen; //累加覆盖面积 update(1,line[i]); //更新线段树 } printf("%.2lf/n",res); } return 0; }