扫描线
建议移步fsy的博客
讲的比较清楚
注意判一下边界条件
丢个板
\(2019/10/3 UPD:\)
还是决定过来补一个档,讲得可能比较略
我们知道扫描线是用来求解矩形面积并的,那么我们考虑维护一根直线从左到右(或从下到上)扫过整个平面,那么显然直线被矩形们覆盖的长度只在每次经过一个矩形的边界时才会改变
对于每次长度的改变分开考虑
设每一段被矩形覆盖的长度为\(a_i\),扫过(即存在过的)长度为\(\Delta L_i\)
那么最后答案为\(\sum (a_i * \Delta L_i)\)
考虑怎么维护呢
在\(y\)轴上建立一棵线段树维护线段
在线段树的每个节点上记录两个值,\(cnt\)和\(len\),此节点表示的区间被覆盖的次数为\(cnt\),长度为\(len\)
考虑怎么上传标记:如果\(cnt(p)>0\),说明这个节点表示的区间已经被整段覆盖,\(len(p) = Y[r(p)] - Y[l(p)]\)
否则\(len(p)\)为两子节点\(cnt\)值之和
注意因为线段树维护的是线段而非点,一些边界条件和常规线段树不同以规避端点和中点的重复和遗漏,看代码理解
#include<bits/stdc++.h>
#define int long long
#define N (300000 + 10)
using namespace std;
inline void read(int &cnt) {
cnt = 0;
int f = 1; char c = getchar();
while (!isdigit(c)) {if (c == '-') f = -f; c = getchar();}
while (isdigit(c)) {cnt = (cnt << 3) + (cnt << 1) + (c ^ 48); c = getchar();}
cnt *= f;
}
int n, tot = 0, ans = 0;
int a, b, c, d, cnt;
int Y[N << 1];
struct Line {
int x, Y1, Y2;
int k;
}seg[N << 1];
bool cmp(Line a, Line b) {return a.x == b.x ? a.Y1 < b.Y1 : a.x < b.x;}
struct node {
int l, r;
int cnt, len;
#define l(p) tree[p].l
#define r(p) tree[p].r
#define cnt(p) tree[p].cnt
#define len(p) tree[p].len
}tree[N << 2];
void pushup(int p) {
if (cnt(p) > 0) len(p) = Y[r(p)] - Y[l(p)];
else len(p) = len(p << 1) + len(p << 1 | 1);
}
void build(int p, int l, int r) {
// cout<<p<<endl;
l(p) = l, r(p) = r;
if (l == r - 1) {len(p) = cnt(p) = 0; return;}
int mid = (l + r + 1) >> 1;
build(p << 1, l, mid);
build(p << 1 | 1, mid, r);
// pushup(p);
}
void modify(int p, int l, int r, int k) {
if (l <= Y[l(p)] && r >= Y[r(p)]) cnt(p) += k;
else {
int mid = (l(p) + r(p) + 1) >> 1;
if (l < Y[mid]) modify(p << 1, l, r, k);
if (r > Y[mid]) modify(p << 1 | 1, l, r, k);
}
pushup(p);
}
signed main() {
read(n);
for (register int i = 1; i <= n; ++i) {
read(a), read(b), read(c), read(d);
seg[++cnt].x = a, seg[cnt].Y1 = b, seg[cnt].Y2 = d, seg[cnt].k = 1, Y[cnt] = b;
seg[++cnt].x = c, seg[cnt].Y1 = b, seg[cnt].Y2 = d, seg[cnt].k = -1, Y[cnt] = d;
}
sort(seg + 1, seg + cnt + 1, cmp);
sort (Y + 1, Y + cnt + 1);
tot = unique(Y + 1, Y + cnt + 1) - Y - 1;
build(1, 1, tot);
for (register int i = 1; i <= cnt - 1; ++i) {
modify(1, seg[i].Y1, seg[i].Y2, seg[i].k);
ans += (seg[i + 1].x - seg[i].x) * len(1);
// cout<<ans<<endl;
} printf("%lld", ans);
return 0;
}