1. 程式人生 > 實用技巧 >AcWing247亞特蘭蒂斯(線段樹+掃描線)

AcWing247亞特蘭蒂斯(線段樹+掃描線)

題目地址https://www.acwing.com/problem/content/249/

題目描述

有幾個古希臘書籍中包含了對傳說中的亞特蘭蒂斯島的描述。

其中一些甚至包括島嶼部分地圖。

但不幸的是,這些地圖描述了亞特蘭蒂斯的不同區域。

您的朋友Bill必須知道地圖的總面積。

你自告奮勇寫了一個計算這個總面積的程式。

輸入格式

輸入包含多組測試用例。

對於每組測試用例,第一行包含整數n,表示總的地圖數量。

接下來n行,描繪了每張地圖,每行包含四個數字x1,y1,x2,y2(不一定是整數),(x1,y1)(x2,y2)分別是地圖的左上角位置和右下角位置。

注意,座標軸 x 軸從上向下延伸,y 軸從左向右延伸。

當輸入用例n=0時,表示輸入終止,該用例無需處理。

輸出格式

每組測試用例輸出兩行。

第一行輸出”Test case #k”,其中k是測試用例的編號,從1開始。

第二行輸出“Total explored area: a”,其中a是總地圖面積(即此測試用例中所有矩形的面積並,注意如果一片區域被多個地圖包含,則在計算總面積時只計算一次),精確到小數點後兩位數。

在每個測試用例後輸出一個空行。

資料範圍

1n10000
0x1<x2100000
0y1<y2100000

題解:這是一道掃描線+線段樹的問題。

我們可以將矩形轉變為左邊+1,右邊-1的操作,每次只需要看看當前大於0的區間的長度再乘以寬度就是當前部分的面積,將所有的面積加起來就可以了

但是因為縱座標可以不是整數,所以如果直接線段樹的話不可以的,首先需要進行離散化處理,將所有的縱座標進行離散化。這個可以不使用pushdown(),首先pushdown()是在query()和modify()中使用,而我們所要查詢的直接tr[u].len就可以了。至於modify(),因為+1和減1是成對出現的,所以不必向下傳遞。

AC程式碼

#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
struct segment{
    double x,y1,y2;//儲存的是一個縱座標區間 
    int k;
}seg[N
*2]; struct node { int l,r; int cnt;//表示當前區間已經被覆蓋了多少次,但是並不向子節點傳遞, double len;//表示當前區間大於0的長度 }tr[8*N]; vector<double>all;//用於區間橫座標的離散化 int cmp(struct segment a1,struct segment a2){ return a1.x<a2.x; } int find(double y){//返回all中第一個大於等於y的下標再加1,因為原下標是從0開始,而我們需要的是從1開始 return lower_bound(all.begin(),all.end(),y)-all.begin()+1; } void pushup(int u){//向上更新 if(tr[u].cnt) tr[u].len=all[tr[u].r]-all[tr[u].l-1]; else if(tr[u].l!=tr[u].r) tr[u].len=tr[u<<1].len+tr[u<<1|1].len; else tr[u].len=0; } void build(int u,int l,int r){//構建線段樹 tr[u].l=l; tr[u].r=r; tr[u].cnt=0; tr[u].len=0; if(l==r){ return ; } int mid=l+r>>1; build(u<<1,l,mid); build(u<<1|1,mid+1,r); return ; } void modify(int u,int l,int r,int d){//區間修改,這裡需要注意的是修改[l,r]實際代表的是讓all[l-1],all[r]的區間修改 if(tr[u].l>=l&&tr[u].r<=r){ tr[u].cnt+=d; pushup(u); return ; } int mid=tr[u].l+tr[u].r>>1; if(l<=mid) modify(u<<1,l,r,d); if(r>mid) modify(u<<1|1,l,r,d); pushup(u); return ; } int main(){ int n,T=0; while(~scanf("%d",&n)){ if(!n) return 0; all.clear(); for(int i=1,j=1;i<=n;i++){ double x1,y1,x2,y2; scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2); seg[j++]={x1,y1,y2,1}; seg[j++]={x2,y1,y2,-1}; all.push_back(y1),all.push_back(y2); } sort(seg+1,seg+2*n+1,cmp); sort(all.begin(),all.end()); all.erase(unique(all.begin(),all.end()),all.end()); build(1,1,all.size()-1); double ans=0; for(int i=1;i<=n*2;i++){ if(i>1) ans+=(double)tr[1].len*(seg[i].x-seg[i-1].x); modify(1,find(seg[i].y1),find(seg[i].y2)-1,seg[i].k); } cout<<"Test case #"<<++T<<endl; printf("Total explored area: %.2lf\n\n",ans); } return 0; }

寫於:202/8/29 12:34