P8648 油漆面积
hatsune___miku · · 题解
P8648 油漆面积
upd:被 hack 了,应将数组开大点,感谢 @rui_er 的指出。
扫描线的板子题,不会的可以先写一下这道题。
题意简述
给定
扫描线
扫描线顾名思义就是有一条线自底向上扫动(其实怎么扫都无所谓),通常用来求矩形所覆盖的面积或周长。
上面这张图很好的演示了扫描线的全过程,我们可以把整个矩形分成若干个小矩形,这些小矩形的高就是扫描线扫过的距离,而小矩形的长就可以用线段树来维护。
具体地,线段树的节点需要维护两个值,分别是
-
若
cnt>0 ,不管覆盖多少次,len 一定就是当前节点的区间长度。 -
若
cnt=0 ,len 就为左右子节点的len 的和。
void update(int p) {
if (t[p].cnt) t[p].len = a[t[p].r+1]-a[t[p].l];
else t[p].len = t[p<<1].len+t[p<<1|1].len;
}
有了这段代码,剩下的就是线段树的板子了,于是就可以愉快的切过这道题了。
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e4+5;
int n, tot, a[N<<2];
ll ans;//十年oi一场空,不开long long见祖宗
struct edge {
int y, x1, x2, k;
} e[N<<1];
struct SegmentTree {
int l, r;
ll len, cnt;
} t[N<<3];
void build(int p, int l, int r) {
t[p].l = l, t[p].r = r;
if (l == r) return;
int mid = (l+r)>>1;
build(p<<1, l, mid);
build(p<<1|1, mid+1, r);
}
void update(int p) {//向上传递
if (t[p].cnt) t[p].len = a[t[p].r+1]-a[t[p].l];
else t[p].len = t[p<<1].len+t[p<<1|1].len;
}
void change(int p, int l, int r, int x) {
if (l <= t[p].l && t[p].r <= r) {
t[p].cnt += x;
update(p);
return;
}
int mid = (t[p].l+t[p].r)>>1;
if (l <= mid) change(p<<1, l, r, x);
if (mid < r) change(p<<1|1, l, r, x);
update(p);
}
bool cmp(edge x, edge y) {return x.y < y.y;}
int query(int x) {return lower_bound(a+1, a+tot+1, x)-a;}//查询
int main() {
cin >> n;
for (int i = 1; i <= n; ++i) {
int x1, y1, x2, y2;
cin >> x1 >> y1 >> x2 >> y2;
e[(i<<1)-1] = (edge){y1, x1, x2, 1};
e[i<<1] = (edge){y2, x1, x2, -1};
a[(i<<1)-1] = x1, a[i<<1] = x2;
}
n <<= 1;
sort(e+1, e+n+1, cmp);
sort(a+1, a+n+1);
tot = unique(a+1, a+n+1)-a-1;//离散化
build(1, 1, tot);
for (int i = 1; i <= n; ++i) {
change(1, query(e[i].x1), query(e[i].x2)-1, e[i].k);
ans += t[1].len*(e[i+1].y-e[i].y);
}
cout << ans;
return 0;
}
P8734,P1856,P1502,P5490 这些都是扫描线的题,有兴趣可以做一做。