HDU1255 覆盖的面积 (线段树求面积交)

来源:互联网 发布:黑客技术软件网盘 编辑:程序博客网 时间:2024/05/27 21:13

覆盖的面积

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 6242    Accepted Submission(s): 3149


Problem Description
给定平面上若干矩形,求出被这些矩形覆盖过至少两次的区域的面积.


 

Input
输入数据的第一行是一个正整数T(1<=T<=100),代表测试数据的数量.每个测试数据的第一行是一个正整数N(1<=N<=1000),代表矩形的数量,然后是N行数据,每一行包含四个浮点数,代表平面上的一个矩形的左上角坐标和右下角坐标,矩形的上下边和X轴平行,左右边和Y轴平行.坐标的范围从0到100000.

注意:本题的输入数据较多,推荐使用scanf读入数据.
 

Output
对于每组测试数据,请计算出被这些矩形覆盖过至少两次的区域的面积.结果保留两位小数.
 

Sample Input
251 1 4 21 3 3 72 1.5 5 4.53.5 1.25 7.5 46 3 10 730 0 1 11 0 2 12 0 3 1
 

Sample Output
7.630.00
 

Author
Ignatius.L & weigang Lee


思路:定义一个变量cnt,表示该区间是否被完全覆盖,s表示区间覆盖1次或以上的长度,ss表示区间覆盖2次及以上的长度,然后分类讨论:

          cnt > 1 表示该区间被覆盖2次及以上,则该区间完全被覆盖,区间的长度则为覆盖的长度,其他的情况则就是cnt==1和cnt==0或者l == r

  cnt = 1 表示该区间被覆盖1次或以上,则该区间的长度等于其左区间覆盖一次或以上的长度+右区间覆盖一次或以上的。

          cnt = 0 则该区间的长度等于其左区间覆盖两次或以上+右区间覆盖两次或以上的长度

附一大佬博客链接:http://www.cnblogs.com/scau20110726/archive/2013/04/14/3020998.html


AC代码如下:

#include <cstdio>#include <cstring>#include <algorithm>#define ls rt << 1#define rs rt << 1 | 1using namespace std;const int maxn = 1010;struct LINE{double l,r,h;//l,r表示某条线段的左右端点的坐标,h表示高度int flag; //flag=1表示下边,flag=-1表示上边}line[maxn << 3];struct node{int l,r,cnt; //l,r表示以rt为根节点的左右区间,cnt记录当前重叠几次double s,ss;//s表示重叠1次及以上的长度,ss表示重叠2次及以上的长度}p[maxn << 3];  //该范围至少要开*8的空间int n,t;double x[maxn << 3];bool cmp(LINE A, LINE B){return A.h < B.h;}void build(int l, int r, int rt){p[rt].l = l; p[rt].r = r;p[rt].cnt = p[rt].s = p[rt].ss = 0;if(l == r) return ;int m = (l + r) >> 1;build(l,m,ls);build(m+1,r,rs);}void pushup(int rt){if(p[rt].cnt) p[rt].s = x[p[rt].r + 1] - x[p[rt].l];else if(p[rt].l == p[rt].r) p[rt].s = 0;else p[rt].s = p[ls].s + p[rs].s;    //面积覆盖两次及以上,则等于该区间内的线段长,左端点=右端点则线段长度为0,面积覆盖1次或以上,则等于左右区间的和(保证两次及以上)    //面积覆盖0次或以上,则等于左右覆盖两次或以上的区间和if(p[rt].cnt > 1) p[rt].ss = x[p[rt].r + 1] - x[p[rt].l];else if(p[rt].r == p[rt].l) p[rt].ss = 0;else if(p[rt].cnt == 1) p[rt].ss = p[ls].s + p[rs].s;else p[rt].ss = p[ls].ss + p[rs].ss;}void update(int l, int r, int rt, int flag){if(p[rt].l == l && p[rt].r == r){//找到该线段p[rt].cnt += flag;pushup(rt);return;}int m = (p[rt].l + p[rt].r) >> 1;if(r <= m) update(l,r,ls,flag);else if(l > m) update(l,r,rs,flag);else{update(l,m,ls,flag);update(m+1,r,rs,flag);}pushup(rt);}int main(){scanf("%d",&t);while(t --){scanf("%d",&n);double x1,x2,y1,y2;int tot = 0;for(int i = 0; i < n; i ++){scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);x[tot] = x1; x[tot+1] = x2;line[tot].h = y1; line[tot+1].h = y2;line[tot].flag = 1; line[tot+1].flag = -1;line[tot].l = line[tot+1].l = x1;line[tot].r = line[tot+1].r = x2;tot += 2;}sort(x,x+tot);sort(line,line+tot,cmp);int k = 1;for(int i = 1; i < tot; i ++){ //离散化,去掉重复的坐标if(x[i] != x[i-1]) x[k ++] = x[i];}build(0,k-1,1);double ans = 0;for(int i = 0; i < tot-1; i ++){int l = lower_bound(x,x+k,line[i].l) - x;int r = lower_bound(x,x+k,line[i].r) - x - 1;update(l,r,1,line[i].flag);ans += (line[i+1].h - line[i].h) * p[1].ss;}printf("%.2lf\n",ans);}return 0;}