Java後臺管理框架的開源專案
阿新 • • 發佈:2022-05-28
首先考慮這樣一個問題:
給你 \(n\) 個矩形的左上角座標和右下角座標,求他們的面積並。
我會暴力! 其中 \(n <= 1e5\) 座標都是 int
範圍內的數。
我會容斥! 顯然會 TLE
所以我們需要考慮一種更快速更容易維護的方式。
掃描線,顧名思義就是一條平行於座標軸的線從第一個矩形的左邊一直掃描到最後一個矩形的右邊。
考慮維護每次掃描線碰到的矩形的邊長,只需要將這次碰到的邊長乘上這次碰到的邊的橫座標與上次碰到的邊的橫座標的差就可以算出兩端掃描線之間的距離。
那麼如何維護掃描線掃到的矩形的邊長呢?
可以使用一個變數記錄這條邊是矩形的左邊還是右邊,因為可以發現的是,掃描線的長度是隨著每次碰到一條矩形的邊而不斷變化的,所以只需要一個變數 cover
cover++
,是右邊就 cover--
。
所以每次掃描線掃到一條新的邊,就只需要更改所有掃描線覆蓋的邊的 cover
值即可。
這種操作不就是線段樹的區間修改操作嗎?
整理一下思路,首先我們在 \(y\) 軸上建立線段樹維護掃描線覆蓋的矩形的邊長,每次進行區間修改並累加答案即可。
需要注意的是,由於座標的數值很大,所以我們需要對座標進行離散化操作。
struct Line{ int x; int y1,y2; int state; bool operator < (Line a){ return x < a.x; } }line[N]; struct Node{ int l,r; int cover; long long len; }tree[N<<3]; inline void push_up(int p){ if(tree[p].cover) tree[p].len = tree[p].r - tree[p].l; else tree[p].len = tree[p<<1].len+tree[p<<1|1].len; } void build(int p,int l,int r){ tree[p].l = v[l] , tree[p].r = v[r]; if(r - l <= 1) return; int mid = l + r >>1; build(p<<1,l,mid); build(p<<1|1,mid,r); } void modify(int p,int l,int r,int k){ if(l <= tree[p].l && tree[p].r <= r){ tree[p].cover += k; push_up(p); return; } if(l < tree[p<<1].r) modify(p<<1,l,r,k); if(r > tree[p<<1|1].l) modify(p<<1|1,l,r,k); push_up(p); } int main(){ int n = read(); for(int i=1;i<=n;i++){ int a = read() , b = read() , c = read() , d = read(); v[i] = b,v[n+i] = d; line[i] = (Line){a,b,d,1} , line[n+i] = (Line){c,b,d,-1}; } sort(v+1,v+(n<<1)+1); sort(line+1,line+(n<<1)+1); build(1,1,n<<1); unsigned long long ans = 0; for(int i=1;i<=n<<1;i++){ ans += tree[1].len * (line[i].x - line[i-1].x); modify(1,line[i].y1,line[i].y2,line[i].state); } printf("%llu\n",ans); return 0; }