1. 程式人生 > >HDU - 1542 Atlantis (掃描線+線段樹)

HDU - 1542 Atlantis (掃描線+線段樹)

date 坐標 lag bool ise each ack ons union

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.

InputThe 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.OutputFor 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 

題意:給出n個矩形,和它們的左下角和右上角的坐標,求所有矩形的覆蓋面積

分析:掃描線的經典題,用線段樹維護當前狀態覆蓋的橫坐標的區間總和,這樣到下一條掃描線為止的高度和它的乘積,一定是被覆蓋的面積。
由於題目的坐標是浮點數,所以首先需要進行離散化處理,對應到整型數上
然後用[l,r]代表l到r+1的區間,因為要讓線段樹的每個節點都覆蓋一個區間,所以[l,l]代表[l,l+1]的區間
線段樹節點還需要維護區間的flag值,將矩形的橫向邊,分為入邊和出邊,當遇到入邊時,flag++,遇到出邊時flag--,當flag>0時,就代表當前節點的區間被覆蓋
這樣樹的根節點的權值,就代表總的被覆蓋區間的和,再乘以掃描線高度的差,即為當前狀態下被覆蓋的面積,進行累加即可

關於掃描線,這篇博客講的很詳細:https://blog.csdn.net/u013480600/article/details/22548393

代碼如下:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
const int MAXN=1010;
typedef long long ll;
vector<double>V;
int get_id(double x) //將浮點數進行離散化處理
{
  return lower_bound(V.begin(),V.end(),x)-V.begin();
}

struct node2 { double l,r; double h; int flag; }line[MAXN]; double sum[MAXN<<2];//儲存該節點區間的被覆蓋的長度 double tot[MAXN<<2];//儲存該節點區間的總的長度 struct node { int l; int r; int f; }tree[MAXN<<2]; void PushUp(int rt) { sum[rt]=sum[rt<<1]+sum[rt<<1|1]; tot[rt]=tot[rt<<1]+tot[rt<<1|1]; } void BuildTree(int l,int r,int rt) { tree[rt].l=l; tree[rt].r=r; tree[rt].f=0; if(l==r) { tot[rt]=V[l+1]-V[l]; sum[rt]=0; return; } int mid=(tree[rt].l+tree[rt].r)/2; BuildTree(l,mid,rt<<1); BuildTree(mid+1,r,rt<<1|1); PushUp(rt); } void Update(int c,int l,int r,int rt) { if(tree[rt].l==l&&tree[rt].r==r) { tree[rt].f+=c; // cout<<V[l]<<" "<<V[r+1]<<" "<<tree[rt].f<<endl; if(tree[rt].f>0) sum[rt]=tot[rt]; else sum[rt]=0; } if(tree[rt].l==tree[rt].r) return; int mid=(tree[rt].l+tree[rt].r)/2; if(r<=mid) Update(c,l,r,rt<<1); else if(l>mid)Update(c,l,r,rt<<1|1); else { Update(c,l,mid,rt<<1); Update(c,mid+1,r,rt<<1|1); } PushUp(rt); } bool cmp(node2 a,node2 b) { if(a.h!=b.h) return a.h<b.h; else if(a.l!=b.l) return a.l<b.l; return a.r<b.r; } int n,cnt,Case=0;; double x1,x2,y1,y2,ans; int main() { while(scanf("%d",&n)&&n) { Case++; ans=0; cnt=0; V.clear(); V.push_back(-1); for(int i=1;i<=n;i++) { scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2); ++cnt; line[cnt].l=x1,line[cnt].r=x2,line[cnt].h=y1,line[cnt].flag=1;//分出所有入邊 ++cnt; line[cnt].l=x1,line[cnt].r=x2,line[cnt].h=y2,line[cnt].flag=-1;//分出所有出邊 V.push_back(x1); V.push_back(x2); } sort(V.begin(),V.end()); V.erase(unique(V.begin(),V.end()),V.end()); sort(line+1,line+cnt+1,cmp);//將邊按縱坐標進行排列 int len=V.size()-1; BuildTree(1,len,1); // cout<<cnt<<endl; for(int i=1;i<=cnt;i++) { if(i>1) ans+=sum[1]*(line[i].h-line[i-1].h); Update(line[i].flag,get_id(line[i].l),get_id(line[i].r)-1,1); // cout<<sum[1]<<" "<<ans<<endl; } printf("Test case #%d\n",Case); printf("Total explored area: %.2f\n\n",ans); } return 0; }

HDU - 1542 Atlantis (掃描線+線段樹)