题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1542
题意 :给出几个矩形坐标的对角线,求矩形其覆盖面积。
对这样的一个矩形,我们构造两条线段,左条设为x1,它在y坐标的区间是[y1,y2],并且给定一个cover域值为1;另一条线段设为x2,区间一样是[y1,y2],给定它一个cover值为-1。根据这样的方法对每个矩形都构造两个线段,最后将所有的线段x从左到右进行排序。
样例矩形如图所示:
开始时扫描线的位置:
我们设矩形左端的flag值为1,右端为-1;
将x坐标离散化,按顺序将x标号作为线段树区间;
现在开始扫描,设每一个线段树区间有一个cover值来记录该区间有多少线段,刚开始cover值都为0;当x = 10插入线段树之后,我们将线段树上的cover加上该线段的flag,那么,此时线段树上被该线段覆盖的位置上的cover的值就为1;x = 20同理
当插入x = 15时区间cover已经为1,计算并面积,并更改cover值,cover+=flag;
当x = 20时区间cover为1,计算并面积,并更改cover值;
最后,也就是说,我们插入某跟线段的时候,只要看该线段所在区间上的cover是否大于等于1,如果是,那么就可以将并面积值加上(目前线段的位置 - 上一线段的位置)*(该区间的大小)
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<string>
#include<cstring>
#include<cmath>
#include<map>
#include<iomanip>
#include<set>
#define inf 0x3f3f3f3f
using namespace std;
double y[222]; //保存纵坐标
struct line
{
double x; //线段的横坐标
double y_up,y_down;//线段的上端点和下端点
int flag ;//记录该线段是左线段还是右线段
bool operator < (const line &b)const{ //将x升序排序
return x < b.x;
}
}Line[222];//线数组
struct tree
{
double x; //该区间线段的横坐标
double y_up,y_down;//该区间线段的上下端点
int cover; //该区间有多少线段
bool isLeaf; //是否为叶子节点
}Tree[111111]; // 线段树数组
void build(int i , int l , int r)//i:数组下标;l:区间左值下标;r:区间右值下标.
{
Tree[i].x = -1; //-1表示该区间没有线段。
Tree[i].cover = 0;//表示该区间有多少线段。左线段加进去++1,右线段加进去--1。
Tree[i].y_up = y[r]; //该区间的上端点
Tree[i].y_down = y[l];//该区间的下端点
Tree[i].isLeaf = false; //不是叶子节点
if(l + 1 == r)
{
//是叶子结点则标记后终止。
Tree[i].isLeaf = true;
return;
}
int mid = (l + r) / 2;
build(i * 2 , l , mid);
build(i * 2 +1 , mid , r);//注意:mid而不是mid+1.
}
double Insert(int i , double x , double y_up , double y_down , int flag )
{
/*i:数组下标;x,y_down,y_up:线段各值;flag:标记左线段还是右线段。*/
if(y_up <= Tree[i].y_down || y_down >= Tree[i].y_up)return 0;
//要插入的线段完全与该段线不搭嘎。
if(Tree[i].isLeaf) //若是叶子结点。
{
if(Tree[i].cover > 0) //且cover大于0,即cover不等于0,表示该区间已有左线段。
{
double s = (x - Tree[i].x)*(Tree[i].y_up - Tree[i].y_down); //计算面积
Tree[i].x = x; //更新横坐标。
Tree[i].cover += flag; //计数
return s;
}
else //cover==0,表示该区间还没有左线段,不能求面积
{
Tree[i].x = x;
Tree[i].cover += flag;
return 0;
}
}
//对叶子结点操作。
double s1 = Insert(i * 2 , x , y_up , y_down , flag);
double s2 = Insert(i *2 + 1 , x , y_up , y_down , flag);
return s1+s2;
}
int main()
{
int n,Case = 1;
double x1,x2,y1,y2;
while(~scanf("%d",&n)&&n) //输入n
{
int k = 0;
for(int i = 1 ; i <= n ; i ++ ) //输入数据。
{
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
Line[++k].x = x1;
Line[k].y_up = y2; //把矩形的竖边化成线存入Line数组。
Line[k].y_down = y1;
y[k] = y1;
Line[k].flag = 1;
Line[++k].x = x2;
Line[k].y_up = y2;
Line[k].y_down = y1;
y[k] = y2;
Line[k].flag = -1;
}
sort(Line + 1, Line + k + 1); //将线按x升序排列 ,注意sort函数的起始终止位置。
sort(y + 1 , y + k + 1); //将所有纵坐标升序排列。
build (1 , 1 , 2 * n); //建树。
double S = 0; //存面积
for(int i = 1 ; i <= 2*n ; i ++ )
{
S += Insert(1 ,Line[i].x , Line[i].y_up , Line[i].y_down,Line[i].flag);
}
printf("Test case #%d\nTotal explored area: %.2lf\n\n",Case++ , S);
}
return 0;
}