覆盖的面积
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读入数据.
注意:本题的输入数据较多,推荐使用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
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 | 1
using 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;
}