00:00:00
扫描线 笔记
看名字很高级,实际上非常好理解的一个算法。
P5490 【模板】扫描线 & 矩形面积并
就是要求平面上若干个矩形,覆盖的面积的总和。

如上图所示,可以沿着红色的线(也就是每个矩形的上下边)将这个面积并分割成三部分。
从下往上看,先将
这一段区间增加 (意义为新增 个图形覆盖这段区间),然后 就是绿色的面积。 接着再看
和 之间,将 这一段区间增加 ,此时 有一个矩形覆盖, 有两个矩形覆盖, 有一个矩形覆盖。同理将 得到蓝色的面积。 接着看
和 之间,到了右侧矩形的上边了,将 这一段区间减 ,此时 有一个矩形覆盖, 得到的就是黄色的面积。 三部分面积相加就是答案。
从上面可以发现,这个算法就是一直在进行区间加和区间减。所以可以考虑用线段树。
将灰线的
但是还有个问题,如果有两段区间分别是
记得线段树的定义改了,最大的区间是
记得开
cpp
#include <bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
using namespace std;
const int N = 1e6 + 10;
int n, a[N];
struct LINE {
int l, r, h, val;
} line[N];
bool cmp(LINE x, LINE y) {
return x.h < y.h;
}
namespace Segtree {
#define ls (rt << 1)
#define rs (rt << 1 | 1)
struct TREE {
int cnt, len;
} tree[N];
void push_up(int rt, int l, int r) {
if (tree[rt].cnt)
tree[rt].len = a[r + 1] - a[l];
else if (l == r) // 叶子节点没被覆盖直接清零,不 push_up。
tree[rt].len = 0;
else
tree[rt].len = tree[ls].len + tree[rs].len;
}
void update(int rt, int l, int r, int x, int y, int v) {
if (a[l] >= y or a[r + 1] <= x)
return;
if (x <= a[l] and a[r + 1] <= y) {
tree[rt].cnt += v;
push_up(rt, l, r);
return;
}
int mid = l + r >> 1;
update(ls, l, mid, x, y, v);
update(rs, mid + 1, r, x, y, v);
push_up(rt, l, r);
}
}
using namespace Segtree;
signed main() {
IOS;
cin >> n;
for (int i = 1, xa, ya, xb, yb; i <= n; i++) {
cin >> xa >> ya >> xb >> yb;
a[i] = xa, a[i + n] = xb;
line[i] = {xa, xb, ya, 1};
line[i + n] = {xa, xb, yb, -1};
}
n <<= 1;
sort(line + 1, line + n + 1, cmp);
sort(a + 1, a + n + 1);
int m = unique(a + 1, a + n + 1) - (a + 1), ans = 0;
for (int i = 1; i < n; i++) {
update(1, 1, m - 1, line[i].l, line[i].r, line[i].val);
ans += tree[1].len * (line[i + 1].h - line[i].h);
}
cout << ans;
return 0;
}