题目链接:传送门
覆盖的面积
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 6058 Accepted Submission(s): 3051
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
没有做过扫描线的可先做 hdu 1542 Atlantis 传送门
思路:先离散化,更新时,更新到被覆盖的区域,或者更新到点,然后在进行扫描线,就行了。
#include<stdio.h>
#include<string.h>
#include<map>
#include<algorithm>
using namespace std;
#define N 1009
map<double,int>q;
struct node
{
int l,r;//区间范围
double chang;//被二次覆盖的区间长度
int inq;//区间被覆盖了多少次
} tree[N*10];
struct nodee
{
double h1,h2;//矩形的两个横坐标
double x;//矩形的左右两边,边的纵坐标
int inq;//是左边的边(1),还是右边的边(-1)
void set_date(double a,double b,double c,int d)
{
h1=a,h2=b,x=c,inq=d;
}
bool operator<(const nodee &q) const
{
return x<q.x;
}
} dis[N*2];
double a[N*2];
void build(int num,int l,int r)//建树
{
tree[num].l=l;
tree[num].r=r;
tree[num].inq=0;
tree[num].chang=0;
if(l==r) return ;
int mid=(l+r)>>1;
build(num<<1,l,mid);
build(num<<1|1,mid+1,r);
}
void up(int num)
{
if(tree[num].inq>1)//被覆盖两次以上才记录长度
tree[num].chang=a[tree[num].r+1]-a[tree[num].l];
else if(tree[num].l==tree[num].r)//没有被覆盖两次以上
tree[num].chang=0;
else //为了最后能将长度,汇总到tree[1].chang。
tree[num].chang=tree[num<<1].chang+tree[num<<1|1].chang;
}
void update(int num,int x,int y,int p)//更新
{
int l=tree[num].l,r=tree[num].r,mid=(l+r)>>1;
if(x<=l&&r<=y)
{
if(tree[num].inq)//该区间被覆盖过
{
tree[num].inq+=p;
up(num);
return ;
}
if(l==r)//该区间没被覆盖过,到达了尽头
{
tree[num].inq+=p;
return ;
}
}
if(x<=mid) update(num<<1,x,y,p);
if(y>mid) update(num<<1|1,x,y,p);
up(num);
}
int main()
{
int n,t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
q.clear();
memset(a,0,sizeof(a));
double x1,y1,x2,y2;
int m=0;
for(int i=0; i<n; i++)
{
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
a[m]=x1;
dis[m++].set_date(x2,x1,y1,1);
a[m]=x2;
dis[m++].set_date(x2,x1,y2,-1);
}
sort(dis,dis+m);
sort(a,a+m);
int mm=unique(a,a+m)-a;//离散化,去重
build(1,0,mm-1);
for(int i=0; i<mm; i++)//离散化,用map数组记录离散化后的所对应的值
q[a[i]]=i;
double sum=0;
for(int i=0; i<m-1; i++)//扫描线
{
update(1,q[dis[i].h2],q[dis[i].h1]-1,dis[i].inq);
sum+=(dis[i+1].x-dis[i].x)*tree[1].chang;
}
printf("%.2lf\n",sum);
}
return 0;
}
/*
9
3
2 0 4 5
0 2 3 4
2 3 3 7
*/
较简单的一种想法:
#include<stdio.h>
#include<string.h>
#include<map>
#include<algorithm>
using namespace std;
#define N 1009
map<double,int>q;
struct node
{
int l,r;//区间范围
int inq;//区间被覆盖了多少次
} tree[N*10];
struct nodee
{
double h1,h2;//矩形的两个横坐标
double x;//矩形的左右两边,边的纵坐标
int inq;//是左边的边(1),还是右边的边(-1)
void set_date(double a,double b,double c,int d)
{
h1=a,h2=b,x=c,inq=d;
}
bool operator<(const nodee &q) const
{
return x<q.x;
}
} dis[N*2];
double a[N*2];
void build(int num,int l,int r)//建树
{
tree[num].l=l;
tree[num].r=r;
tree[num].inq=0;
if(l==r) return ;
int mid=(l+r)>>1;
build(num<<1,l,mid);
build(num<<1|1,mid+1,r);
}
void doo(int num)
{
if(tree[num].inq)
{
int h=tree[num].inq;
tree[num<<1].inq+=h;
tree[num<<1|1].inq+=h;
tree[num].inq=0;
}
}
void update(int num,int x,int y,int p)//更新,每个区间被覆盖了多少次
{
int l=tree[num].l,r=tree[num].r,mid=(l+r)>>1;
if(x<=l&&r<=y)
{
tree[num].inq+=p;
return ;
}
doo(num);
if(x<=mid) update(num<<1,x,y,p);
if(y>mid) update(num<<1|1,x,y,p);
}
double query(int num)//查找长度
{
int l=tree[num].l,r=tree[num].r,mid=(l+r)>>1;
if(tree[num].inq>1)
return a[r+1]-a[l];
if(l==r) return 0;
doo(num);
double sum=0;
sum+=query(num<<1)+query(num<<1|1);
return sum;
}
int main()
{
int n,t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
q.clear();
memset(a,0,sizeof(a));
double x1,y1,x2,y2;
int m=0;
for(int i=0; i<n; i++)
{
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
a[m]=x1;
dis[m++].set_date(x2,x1,y1,1);
a[m]=x2;
dis[m++].set_date(x2,x1,y2,-1);
}
sort(dis,dis+m);
sort(a,a+m);
int mm=unique(a,a+m)-a;//离散化,去重
build(1,0,mm-1);
for(int i=0; i<mm; i++)//离散化,用map数组记录离散化后的所对应的值
q[a[i]]=i;
double sum=0;
for(int i=0; i<m-1; i++)//扫描线
{
update(1,q[dis[i].h2],q[dis[i].h1]-1,dis[i].inq);
double h=query(1);//查找被覆盖两次的区间范围
sum+=(dis[i+1].x-dis[i].x)*h;
}
printf("%.2lf\n",sum);
}
return 0;
}