覆盖的面积
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 5801 Accepted Submission(s): 2899
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
大致和HDU1542求面积并的方法没太大区别,在结构体中加了一个变量存储覆盖大于一次的长度,然后在求父节点的时候多了几组判断条件
依然是扫描线的思想,只是记录一下覆盖两次或两次以上的边,然后多更新了一个t[rt].ss(覆盖多次的边的长度),最后求面积由覆盖多次的边求
具体看代码
#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int inf=0x3f3f3f3f;
const int N=2200;
struct edge
{
double l,r,h;
int f;
edge() {}
edge(double x1,double x2,double y,double op)
{
l=x1,r=x2,h=y,f=op;
}
bool operator < (const edge&A)const
{
return h<A.h;
}
} e[N];
struct node
{
int cnt;
double s,ss;
} t[N<<2];
double pos[N];
void pushdown(int rt,int le,int ri)
{
if(t[rt].cnt)//全部覆盖
t[rt].s=pos[ri+1]-pos[le];
else if(le==ri)//为一个点,长度为0
t[rt].s=0;
else//既不全部覆盖,也不是点,由两边区间得出
t[rt].s=t[rt<<1].s+t[rt<<1|1].s;
if(t[rt].cnt>1)
t[rt].ss=pos[ri+1]-pos[le];
else if(le==ri)
t[rt].ss=0;
else if(t[rt].cnt==1)
t[rt].ss=t[rt<<1].s+t[rt<<1|1].s;
/*
cnt==1代表从le到ri这段(完整的)长度从初始到现在被覆盖了一次,所以在这段区间中被覆盖两次
的长度等于左右子节点中被覆盖一次的长度之和(根节点的覆盖和子节点的覆盖肯定不在同一段线段上)
*/
else
t[rt].ss=t[rt<<1].ss+t[rt<<1|1].ss;
}
void update(int x,int y,int l,int r,int rt,int val)
{
if(x<=l&&y>=r)
{
t[rt].cnt+=val;
pushdown(rt,l,r);
return;
}
int m=(l+r)>>1;
if(x<=m)
update(x,y,l,m,rt<<1,val);
if(y>m)
update(x,y,m+1,r,rt<<1|1,val);
pushdown(rt,l,r);
}
int main()
{
int n,Caes;
double x1,x2,y1,y2;
scanf("%d",&Caes);
while(Caes--)
{
scanf("%d",&n);
mem(t,0);
int tot=0;
for(int i=0; i<n; i++)
{
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
pos[tot]=x1;
e[tot++]=edge(x1,x2,y1,1);
pos[tot]=x2;
e[tot++]=edge(x1,x2,y2,-1);
}
sort(pos,pos+tot);
sort(e,e+tot);
int m=unique(pos,pos+tot)-pos;
double ans=0;
for(int i=0; i<tot; i++)
{
int x=lower_bound(pos,pos+m,e[i].l)-pos;
int y=lower_bound(pos,pos+m,e[i].r)-pos-1;
update(x,y,0,m,1,e[i].f);
ans+=(e[i+1].h-e[i].h)*t[1].ss;
}
printf("%.2f\n",ans);
/*
这里注意下扫描线段时r-1:int R=search(s[i].l,pos,m)-1;
计算底边长时r+1:if(mark[n])sum[n]=pos[right+1]-pos[left];
解释:假设现在有一个线段左端点是l=0,右端点是r=m-1
则我们去更新的时候,会算到sum[1]=pos[mid]-pos[left]+pos[right]-pos[mid+1]
这样的到的底边长sum是错误的,why?因为少算了mid~mid+1的距离,由于我们这利用了
离散化且区间表示线段,所以mid~mid+1之间是有长度的,比如hash[3]=1.2,pos[4]=5.6,mid=3
所以这里用r-1,r+1就很好理解了
*/
}
return 0;
}