hdu 1542 Atlantis (線段樹之掃描線)
http://acm.hdu.edu.cn/showproblem.php?pid=1542
Problem Description
There are several ancient Greek texts that contain descriptions of the fabled island Atlantis. Some of these texts even include maps of parts of the island. But unfortunately, these maps describe different regions of Atlantis. Your friend Bill has to know the total area for which maps exist. You (unwisely) volunteered to write a program that calculates this quantity.
Input
The input file consists of several test cases. Each test case starts with a line containing a single integer n (1<=n<=100) of available maps. The n following lines describe one map each. Each of these lines contains four numbers x1;y1;x2;y2 (0<=x1< x2<=100000;0<=y1< y2<=100000), not necessarily integers. The values (x1; y1) and (x2;y2) are the coordinates of the top-left resp. bottom-right corner of the mapped area.
The input file is terminated by a line containing a single 0. Don’t process it.
Output
For each test case, your program should output one section. The first line of each section must be “Test case #k”, where k is the number of the test case (starting with 1). The second one must be “Total explored area: a”, where a is the total explored area (i.e. the area of the union of all rectangles in this test case), printed exact to two digits to the right of the decimal point.
Output a blank line after each test case.
Sample Input
2
10 10 20 20
15 15 25 25.5
0
Sample Output
Test case #1
Total explored area: 180.00
題目大意:給你幾個矩形的左下角和右上角,求出這幾個矩形的面積,重複部分只算一次。
解題思路:很明顯這個一個求相加矩形面積的題,需要用到掃描線和線段樹結合。下面我們就來說一下掃描線。
首先明白一個定義,我們將矩形的左右兩條邊分別叫做左邊界(即與y軸平行x座標較小的那個邊)和右邊界(即與y軸平行x座標較大的那個邊)如果是左邊界cover要+=1,右邊界cover-+1,(cover的用途後邊再具體解釋)然後我們按照x座標從小到大排序就可以得到我們需要的掃描線。如下圖所示:
圖中4條紅線從左到右分別是掃描線1,掃描線2,掃描線3,掃描線4。掃描線分別儲存這幾個變數 x 掃描線的橫座標 y_up 掃描線的上線 y_down 掃描線的下限 flag 這條掃描線是左邊界還是右邊界,左邊界為-1右邊界為1
然後我們再將y座標離散化來構建線段樹維護我們要求的面積。
其中線段樹中的值有x用來表示當前插入掃描線的上一條掃描線的橫座標y_up 線段樹的左區間y_down線段樹的右區間 flag表示這個節點是否是葉子節點 cover表示線段樹的區間有沒有被覆蓋,若cover>0表示被覆蓋。
當我們插入第一條掃描線時,由於cover值為0,所以沒有計算任何面積
插入第二條掃描線計算面積為圖中陰影部分。因為當插入第二條掃描邊時只有【10,15】部分的cover值大於0
插入第三條掃描線時計算面積為下圖陰影部分,因為插入第三條時【10,15】部分的cover值大於1,與它前面的那一條掃描邊的距離為10,【15,20】部分同理:
插入第四條掃描線時計算面積為:
具體程式碼實現如下:
#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
struct LINE
{
double x;
double y_up;
double y_down;
int flag;
}line[220];
bool cmp(LINE a,LINE b)
{
return a.x<b.x;
}
double y[220];
double x1,y1,x2,y2;
int n,index=0;
struct Tree
{
double x,y_up,y_down;
bool flag;//用來標記是否是葉子節點
int cover;
}tree[1000*110];
void build(int i, int l, int r)
{
tree[i].x = 0; //表示該節點上一條的x的值
tree[i].cover = 0; //表示該區間上有多少條線段;左邊線段加進去則++,右邊線段加進去則--
tree[i].y_down = y[l];
tree[i].y_up = y[r];
tree[i].flag = false;
if(l+1==r)
{
tree[i].flag = true; //flag==true表示達到了葉子節點
return;
}
int mid=(l+r)>>1;
build(2*i, l, mid);
build(2*i+1, mid, r);
}
double insert(int i,double x,double l,double r,int flag)
{
if(r<=tree[i].y_down||l>=tree[i].y_up)///說明查詢的這一條線不在當前節點的區間
return 0;
if(tree[i].flag)
{
if(tree[i].cover > 0)
{
double temp_x=tree[i].x;
double ans=(x-temp_x)*(tree[i].y_up - tree[i].y_down);
tree[i].x = x; //定位上一次的x
tree[i].cover += flag;
return ans;
}
else
{
tree[i].cover += flag;
tree[i].x = x; //定位上一次的x
return 0;
}
}
double ans1, ans2;
ans1 = insert(2*i, x, l, r, flag);
ans2 = insert(2*i+1, x, l, r, flag);
cout<<ans1<<' '<<ans2<<endl;
return ans1+ans2;
}
int main()
{
int count1=0;
while(scanf("%d",&n),n)
{
index=1;
for(int i=1;i<=n;i++)
{
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
y[index]=y1;
line[index].x=x1;
line[index].y_down=y1;
line[index].flag=1;
line[index++].y_up=y2;
y[index]=y2;
line[index].x=x2;
line[index].y_down=y1;
line[index].y_up=y2;
line[index].flag=-1;
index++;
}
sort(y+1,y+index);
sort(line+1,line+index,cmp);
build(1,1,index-1);
double ans=0;
for(int i=1;i<index;i++)
{
cout<<i<<' ';
ans+=insert(1,line[i].x,line[i].y_down,line[i].y_up,line[i].flag);
}
printf("Test case #%d\nTotal explored area: %.2f\n\n", ++count1, ans);
}
return 0;
}