【Luogu P5490】【模板】掃描線
阿新 • • 發佈:2020-07-21
題目大意:
求 \(n\) 個矩形的面積並。
正文:
本題計算面積並在掃描線中較為簡單。拋開離散化,我們著重講掃描線。
與上圖為例,思考怎麼計算它們的幾何並。
暴力
用一個數組來存資訊,覆蓋了就標 1,否則標 0。暴力既好想又好寫,但當座標一大,時空都會超。
容斥
用總面積減去重合面積。但只侷限於重合少的題。
分割圖形
將圖形重新分割成一個個規則的矩形,用線段樹維護一條掃描的線。
如上圖,每掃描到一段,該段面積就是直線上覆蓋的長度乘該段的寬度。
順著這個思路來,用一個四元組 \((x,y_1,y_2,flag)\) 記錄每一條豎線,表示豎線的座標及是否是左右邊界。
線段樹維護掃描線上被覆蓋的長度,每次修改後,更新被覆蓋長度(如下)。
void pushup(int x)
{
if(t[x].cnt) t[x].len = disx[t[x].r + 1] - disx[t[x].l];
else t[x].len = t[x * 2].len + t[x * 2 + 1].len;
}
對於線段樹的標記,這裡引入 李煜東的《演算法競賽》,在本題我們只關心整個掃描線(線段樹根節點)上被覆蓋的長度。四元組又成對出現,所以線段樹區間修改也是重複出現,這樣就沒必要下傳延時標記,而採用更加簡單的做法:線上段樹每個節點上另加維護該節點代表的區間被矩形覆蓋的長度 \(len\),該節點自身被覆蓋的次數 \(cnt\)。對於一個四元組 \((x,y_1,y_2,flag)\)
對於線段樹任意一個節點 \([l,r]\),若 \(cnt>0\),則 \(len\) 就等於兩子節點 \(len\) 之和。在一個節點 \(cnt\) 被修改,以及線段樹傳遞資訊時,我們都按照方法更新 \(len\) 值。根節點 \(len\) 值就是整個掃描線被覆蓋的長度。
程式碼:
const int N = 2000000 + 10; int n; struct SegmentTree { int l, r; ll cnt, len; }t[N << 2]; ll x[N], disx[N], y[N]; struct node { ll xd, xu, y, flag; }a[N << 2]; bool cmp(node a, node b) { if(a.y == b.y) return a.flag > b.flag; return a.y < b.y; } void build(int x, int l, int r) { t[x].l = l, t[x].r = r; if(l == r) return; int mid = (t[x].l + t[x].r) / 2; build(x * 2, l, mid); build(x * 2 + 1, mid + 1, r); } void pushup(int x) { if(t[x].cnt) t[x].len = disx[t[x].r + 1] - disx[t[x].l]; else t[x].len = t[x * 2].len + t[x * 2 + 1].len; } void change(int x, int l, int r, ll k) { if(l <= t[x].l && t[x].r <= r) { t[x].cnt += k; pushup(x); return; } int mid = (t[x].l + t[x].r) / 2; if(l <= mid) change(x * 2, l, r, k); if(r > mid) change(x * 2 + 1, l, r, k); pushup(x); } ll ans; int main() { scanf("%d", &n); int m = n * 2; for (int i = 1; i <= n; i ++) { scanf("%lld%lld%lld%lld", &x[(i << 1) - 1], &y[(i << 1) - 1], &x[(i << 1)], &y[(i << 1)]); a[(i << 1) - 1].xd = a[(i << 1)].xd = x[(i << 1) - 1]; a[(i << 1) - 1].xu = a[(i << 1)].xu = x[(i << 1)]; a[(i << 1) - 1].y = y[(i << 1) - 1]; a[(i << 1)].y = y[(i << 1)]; a[(i << 1) - 1].flag = 1; a[(i << 1)].flag = -1; } sort(x + 1, x + 1 + m); m = unique(x + 1, x + 1 + m) - (x + 1); for (int i = 1; i <= n * 2; i++) { int p1 = lower_bound(x + 1, x + 1 + m, a[i].xd) - x, p2 = lower_bound(x + 1, x + 1 + m, a[i].xu) - x; disx[p1] = a[i].xd, disx[p2] = a[i].xu; a[i].xd = p1; a[i].xu = p2; } m = 2 * n; build(1, 1, m); sort(a + 1, a + 1 + m, cmp); for (int i = 1; i <= m; i++) { change(1, a[i].xd, a[i].xu - 1, a[i].flag); ans += t[1].len * (a[i + 1].y - a[i].y); } printf("%lld\n", ans); return 0; }