Problem Description
给定平面上若干矩形,求出被这些矩形覆盖过至少两次的区域的面积.
Input
输入数据的第一行是一个正整数T(1<=T<=100),代表测试数据的数量.每个测试数据的第一行是一个正整数N(1<=N<=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
思路
这道题的描述与Atlantis比较相似,但是它需要面积被覆盖至少两次。首先的想法是将原先的判断条件改为大于2,却发现普通的扫描线法并没有下传操作,这就会导致在多次覆盖问题时出现问题。于是我们可以思考怎么样考虑到所有的情况,其中一种方法是增加下传操作,将父节点的标记叠加至子节点。此处使用第二种方法,其中较难理解的一步是区间只被覆盖一次时,仍然存在着符合条件的情况:当父节点代表的区间被覆盖一次,则说明它被整体覆盖了一次,此时若其子节点代表的区间同样被覆盖,则同样满足条件,详见代码注释。
代码
#include<map>
#include<stack>
#include<queue>
#include<string>
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#define ls (k<<1)
#define rs (k<<1|1)
#define pb push_back
#define mid ((l+r)>>1)
using namespace std;
const int mod=1e9+7;
const int maxn=1e5+5;
typedef long long ll;
const int inf=0x3f3f3f3f;
int t,n,cnt,len,tot;
double x1,x2,y1,y2,ans,x[maxn];
struct node
{
int num;
double l,r,h;
}a[maxn];
struct edge
{
int tag;
double len1,len2;
}b[maxn];
bool cmp(node a,node b)
{
return a.h<b.h;
}
void uphold(int l,int r,int k)
{
if(b[k].tag)//当前区间被标记,则把长度加上
b[k].len1=x[r+1]-x[l];
else if(l==r)//唯一区间且未被标记(若此区间处于标记状态,则其长度已记录,不会出现区间长度遗漏的情况)
b[k].len1=0;
else
b[k].len1=b[ls].len1+b[rs].len1;//当父节点未被标记且拥有长度时,将其子节点覆盖的长度和作为新的覆盖长度
if(b[k].tag>=2)
b[k].len2=x[r+1]-x[l];//区间被覆盖两次及以上
else if(l==r)
b[k].len2=0;
else if(b[k].tag==1)
b[k].len2=b[ls].len1+b[rs].len1;
else
b[k].len2=b[ls].len2+b[rs].len2;
}
void update(int l,int r,int l1,int r1,int v,int k)
{
if(l>=l1&&r<=r1)
{
b[k].tag+=v;
uphold(l,r,k);
return ;
}
if(l1<=mid)
update(l,mid,l1,r1,v,ls);
if(r1>mid)
update(mid+1,r,l1,r1,v,rs);
uphold(l,r,k);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
scanf("%d",&t);
while(t--)
{
cnt=0;
scanf("%d",&n);
memset(b,0,sizeof b);
for(int i=0;i<n;i++)
{
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
x[cnt]=x1;
a[cnt].l=x1;
a[cnt].r=x2;
a[cnt].h=y1;
a[cnt++].num=1;
x[cnt]=x2;
a[cnt].l=x1;
a[cnt].r=x2;
a[cnt].h=y2;
a[cnt++].num=-1;
}
ans=0;
sort(x,x+cnt);//用于离散化
sort(a,a+cnt,cmp);
len=unique(x,x+cnt)-x;
for(int i=0;i<cnt;i++)
{
ll l=lower_bound(x,x+len,a[i].l)-x;
ll r=lower_bound(x,x+len,a[i].r)-x-1;//线段树指区间,需要减一使其对应
update(0,len,l,r,a[i].num,1);
ans+=b[1].len2*(a[i+1].h-a[i].h);
}
printf("%.2f\n",ans);
}
return 0;
}