扫描线学习笔记

发布时间 2023-08-08 08:49:36作者: SD!LTF

0. 写在前面

扫描线好闪,拜谢扫描线

1. 问题的引入

在一个二维的坐标系上,给出多个矩形,求他们的面积并

2. 问题的分析

假设我们有这么一张图

你要求这三个矩形的面积并,可以考虑容斥原理,但这样会 TLE

但总之,他最终的结果是围成了一个多边形

那你不妨考虑,重新分割这个最终的图形

那这样你就很会求了。

考虑有一条线,从下往上扫描,如果遇到矩形的一条边就分割一次,OI Wiki 给出了一个很优秀的动图:

那么,你即是要考虑矩形的下端在哪里,矩形的上端在哪里,矩形的高是扫描线扫描的距离。我们使用线段树维护矩形的长,给每个矩形的上下边进行标记。下面标记为 \(1\) , 上面标记为 \(-1\),然后乘一下就好了。

#include <iostream>
#include <algorithm>
#include <cstring>
#define MAXN 1000001
#define int long long
#define ls(x) (x << 1)
#define rs(x) ((x << 1) + 1)
#define mid(l, r) ((l + r) >> 1)

using namespace std;

int lazy[MAXN << 3]; // 用于标记某条线段出现的次数
int s[MAXN << 3];
struct { int l, r, sum; } 	  sgmt[MAXN << 3];
struct node{ 
	int d, y1, y2, flag; 
	bool operator < (const node& x) const { return this->d < x.d;}
	node(int d, int y1, int y2, int flag) {	this->d = d, this->y1 = y1, this->y2 = y2, this->flag = flag; }
	node() {}
} point[MAXN << 3];

inline void pushup(int now) {
	if(lazy[now] > 0)  sgmt[now].sum = sgmt[now].r       - sgmt[now].l;
	else		       sgmt[now].sum = sgmt[ls(now)].sum + sgmt[rs(now)].sum;
}

void build(int now, int l, int r) {
	if(r - l > 1) sgmt[now].l = s[l], sgmt[now].r = s[r], build(ls(now), l, mid(l, r)), build(rs(now), mid(l, r), r), pushup(now);
	else 		  sgmt[now].l = s[l], sgmt[now].r = s[r], sgmt[now].sum = 0;
}

void upd(int now, int y1, int y2, int flag) {
	if(sgmt[now].l == y1 && sgmt[now].r == y2) lazy[now] += flag, pushup(now);
	else {
		if(sgmt[ls(now)].r > y1)   upd(ls(now), y1, min(sgmt[ls(now)].r, y2), flag);
		if(sgmt[rs(now)].l < y2)   upd(rs(now), max(sgmt[rs(now)].l, y1), y2, flag);
		pushup(now);		
	}
}
int n, ans;
signed main() {
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	cin >> n;
	for(int i = 0, x, y, u, v; i < n; ++ i) {
		cin >> x >> y >> u >> v;
		point[i] = {x, y, v, 1}, point[i + n] = {u, y, v, -1};
		s[i + 1] = y, s[i + 1 + n] = v;
	}
	sort(s + 1, s + rs(n)), sort(point, point + ls(n));
	build(1, 1, ls(n));
	upd(1, point[0].y1, point[0].y2, point[0].flag);
	for(int i = 1; i < ls(n); ++ i) {
		ans += (point[i].d - point[i - 1].d) * sgmt[1].sum;
		upd(1, point[i].y1, point[i].y2, point[i].flag);
	}
	cout << ans;
}