掃描線+線段樹詳解
阿新 • • 發佈:2021-08-09
前言
這是我的第2篇cnblog部落格,詳細地解說了掃描線+線段樹的工作原理以及程式碼。
前置知識
- 離散化
- 線段樹
沒有學過前置知識的同學可以先學習一下在回來學習這篇部落格。
Atlantis問題
我們先來看一道掃描線最經典的例題:Atlantis(或者這個)
題意:給你\(n\)個矩形(\(n\le 100\)),它們有可能重疊在一起,求它們一共覆蓋的面積。
掃描線,顧名思義就是一條線在圖上掃描。
話不多說,先看圖(可能有億點醜):
這個圖中,紫色的線條就是掃描線,將掃描線從下往上掃描每一次計算兩個掃描線之間的面積(很好計算,因為是矩形)。
設兩條掃描線是\(y=y_1\)和\(y=y_2\)
而這時候,我們利用類似差分的思想,將每一個矩形的下面打上\(1\)的標籤,上面打上\(-1\)的標籤,用線段樹維護區間和即可。
下面是程式碼(碼風有點醜):
#include<iostream> #include<vector> #include<algorithm> using namespace std; const int maxn=220; struct Line { double x1,x2,y; int flag; }; struct Node { int l,r,flag; double sum; }tree[maxn<<2]; vector<Line> line; vector<double> vx; Line makeline(double x1,double x2,double y,int flag) { Line ret; ret.x1=x1; ret.x2=x2; ret.y=y; ret.flag=flag; return ret; } bool cmp(Line a,Line b) { return a.y<b.y; } int get(double x) { return lower_bound(vx.begin(),vx.end(),x)-vx.begin()+1; } void pushup(int x) { if(tree[x].flag>0) { tree[x].sum=vx[tree[x].r]-vx[tree[x].l-1]; } else { tree[x].sum=tree[x*2].sum+tree[x*2+1].sum; } } void build(int x,int l,int r) { if(l>r) return; tree[x].l=l,tree[x].r=r; tree[x].flag=tree[x].sum=0; if(l==r) return; int mid=(l+r)/2; build(x*2,l,mid); build(x*2+1,mid+1,r); } void update(int x,int l,int r,int delta) { if(l>r) return; if(tree[x].l>=l&&tree[x].r<=r) { tree[x].flag+=delta; pushup(x); return; } int mid=(tree[x].l+tree[x].r)/2; if(l<=mid) { update(x*2,l,r,delta); } if(r>mid) { update(x*2+1,l,r,delta); } pushup(x); } int n; int main() { int t=0; while(scanf("%d",&n)) { if(!n) break; ++t; line.clear(); vx.clear(); for(int i=1;i<=n;i++) { double x1,x2,y1,y2; scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2); line.push_back(makeline(x1,x2,y1,1)); line.push_back(makeline(x1,x2,y2,-1)); vx.push_back(x1); vx.push_back(x2); } sort(vx.begin(),vx.end()); vx.resize(unique(vx.begin(),vx.end())-vx.begin()); sort(line.begin(),line.end(),cmp); build(1,1,(int)vx.size()-1); update(1,get(line[0].x1),get(line[0].x2)-1,line[0].flag); double ans=0; for(int i=1;i<(int)line.size();i++) { ans+=(double)tree[1].sum*(line[i].y-line[i-1].y); update(1,get(line[i].x1),get(line[i].x2)-1,line[i].flag); } printf("Test case #%d\nTotal explored area: %0.2f\n\n",t,ans); } return 0; }
本文來自部落格園,作者:jpy_cpp,轉載請註明原文連結:https://www.cnblogs.com/jpy-cpp/p/15117906.html