掃描線算法的核心思想在於使用線段樹對掃描線線段的長度進行維護。因此,如果平行於x軸做掃描線,那麼就需要以所有的端點的x座標爲端點,以這些端點組成的線段爲線段樹葉子節點存儲的對象,從而對掃描線的長度進行維護。
另外,說明下代碼中cnt的作用。這個標記代表看似是一個lazy tag,但又不是,因爲這個標記是不會往子節點傳的(及不能使用pushdown操作)。掃描線算法中,葉子節點(代表的是每一個小段)和非葉子節點(代表幾個小段的拼接)的cnt值是獨立的,代表的按照線段樹的分法,恰好被完全覆蓋的次數(前提是父節點沒有被完全覆蓋)。因此,如果當前節點的cnt值爲0了,至要它還有子節點,它的sum依然可以由子節點求和得到(雖然整段覆蓋的沒有了,但還可能在這個區間中有部分的覆蓋存在)。
下圖爲cnt的作用。圖了顏色代表cnt加了1.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int INF = 2e9;
const int MAXN = 2e5+5;
struct line {
double x1, x2, y;
int flag;
line(){}
line(double x1, double x2, double y, int flag){
this->x1 = x1, this->x2 = x2, this->y = y; this->flag = flag;
}
friend bool operator < (const line &a, const line &b){
return a.y < b.y;
}
};
vector<double> sum(MAXN << 3, 0);
vector<int> cnt(MAXN << 3, 0);
line lines[MAXN <<1];
double P[MAXN<<1];
void pushUp(int id, int l, int r){
if(cnt[id]){ sum[id] = P[r] - P[l - 1];}
//後面都是cnt爲0的情況,cnt爲1代表區域中所有的邊都被選中
else if(r == l) sum[id] = 0; //只有一段,那麼就直接這一段爲0
else { //有多段,那麼這多段爲後續節點的求和
sum[id] = sum[id << 1] + sum[id << 1|1];
}
}
void update(int id, int l, int r, int L, int R, int flag){
if(L <=l && r <= R){
cnt[id] += flag;
pushUp(id, l, r);
return;
}
int mid = (l + r) >> 1;
if(L <= mid){
update(id << 1, l, mid, L, R, flag);
}
if(R > mid){
update(id << 1|1, mid+1, r, L, R, flag);
}
pushUp(id, l, r);
return;
}
signed main(){
int n; double x1, y1, x2, y2;
int ca = 1;
while(true){
double ans=0;
for(int i = 0; i < sum.size(); i++){sum[i]=0; cnt[i]=0;}
scanf("%d", &n); if(n==0) break;
for(int i = 0; i < n; i++){
scanf("%lf %lf %lf %lf",&x1, &y1, &x2, &y2);
lines[i*2] = line(x1, x2, y1, 1);
lines[i*2+1] = line(x1, x2, y2, -1);
P[i*2] = x1;
P[i*2+1] = x2;
}
sort(lines, lines+2*n);
sort(P, P+2*n);
int d = unique(P, P+2*n) - P; //實際不重合的端點數目
for(int i = 0; i < n * 2 -1; i ++){
int l = lower_bound(P, P+d, lines[i].x1) - P + 1;
int r = lower_bound(P, P+d, lines[i].x2) - P + 1;
update(1, 1, d-1, l, r-1, lines[i].flag);
ans += sum[1] * (lines[i+1].y - lines[i].y);
}
printf("Test case #%d\nTotal explored area: %.2lf\n\n", ca++, ans);
}
return 0;
}