P5490 [模板]扫描线 & 矩形面积并 & P1856 [IOI 1998 / USACO5.5] 矩形周长 Picture 题解
不知道为啥,这个知识点就是学的有点费劲 (
- Pt1. P5490[模板]扫描线 & 矩形面积并
先想一个问题。给你一堆动态变化的线段,让你求它们的公共覆盖面积。
这是不是线段树维护一下就行(?
然后这个线段树的注意点是,由于我们维护的是线段而不是点,线段树节点会出现偏差。比如节点 2, 3 分别记录了 1~3 和 4~6 的信息,然后向上合并的时候就会漏掉 3~4 的情况。
额这个时候要把线段树节点映射的区间,右边界右移一位。这样节点 2,3 分别记录 1~4, 4~6,然后向上合并的时候就好用了。
这个时候看二维扫描线。
流程大概是这样的。先把二维图形抽象成两条边,一条入边,一条出边。横竖都可以,我这里是选择抽象成了两条竖线。然后你考虑一个竖线一个竖线往前走,这样可以把图形分割成若干部分。每个部分的横着的长度(即两条边间的距离)是一样的,因此只需要求出竖着的长度和就行了。
唉?这个是不是就跟最开始的问题一样了?我们每切换一条边,就动态修改一下。然后我们求出公共覆盖面积,再乘上横着的长度就行。
还是放一下代码。
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#define int long long
#define rep(i, a, b) for(int i = a; i <= b; ++i)
using namespace std;
using namespace __gnu_pbds;
gp_hash_table<int, int> mp;
constexpr int N = 100005;
int ans, tot, cnt, n, c[N << 3];
struct Line {
int x, y1, y2, type;
inline bool operator < (const Line rhs) const {
return x < rhs.x;
}
}a[N << 3];
struct Sgement_Tree {
int sum[N << 4], len[N << 4];
void modify(int i, int l, int r, int L, int R, int k) {
if(r < L || l > R) return;
if(L <= l && r <= R) sum[i] += k;
else {
int mid = l + r >> 1;
if(L <= mid) modify(i << 1, l, mid, L, R, k);
if(R > mid) modify(i << 1 | 1, mid + 1, r, L, R, k);
}
if(sum[i]) len[i] = c[r + 1] - c[l];//这个其实是一个双设,相当于映射回去
else len[i] = len[i << 1] + len[i << 1 | 1];
}
}T;
signed main() {
cin >> n;
rep(i, 1, n) {
int x1, y1, x2, y2;
cin >> x1 >> y1 >> x2 >> y2;
a[++cnt] = {x1, y1, y2, 1ll};
a[++cnt] = {x2, y1, y2, -1ll};
c[++tot] = y1, c[++tot] = y2;
}
sort(c + 1, c + tot + 1);
tot = unique(c + 1, c + tot + 1) - c - 1;
rep(i, 1, tot) mp[c[i]] = i;
sort(a + 1, a + cnt + 1);
rep(i, 1, cnt) {
T.modify(1, 1, tot + 1, mp[a[i].y1], mp[a[i].y2] - 1, a[i].type);
//其他按照正常线段树来写,这样找出来的就是本来代表mp[a[i].y1], mp[a[i].y2] - 1的区间,通过右移映射得到现在的区间
ans += T.len[1] * (a[i + 1].x - a[i].x);
}
cout << ans;
return 0;
}
- Pt2. P1856 [IOI 1998 / USACO5.5] 矩形周长 Picture
这两道题其实完全一样(存疑
第二道题甚至比第一道要好想的多。因为你考虑周长的边要不是竖着的,要不是横着的。
那我们横竖分别把它当成拆开的问题,算一下动态的覆盖长度不就好了吗?结果横竖相加即可。
#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = a; i <= b; ++i)
using namespace std;
const int N = 5005;
int lst, ans, n, cnt, cnt1, tot, tot1, b[N << 3], d[N << 3];
struct Line {
int x, y1, y2, type;
inline bool operator < (const Line &rhs) const {
if(x ^ rhs.x) {
return x < rhs.x;
}
return type > rhs.type;
}
}a[N << 3];
struct line {
int y, x1, x2, type;
inline bool operator < (const line &rhs) const {
if(y ^ rhs.y) {
return y < rhs.y;
}
return type > rhs.type;
}
}c[N << 3];
struct Segemnt_Tree {
int len[N << 4], sum[N << 4];
inline void modify(int i, int l, int r, int L, int R, int k, int e[]) {
if(r < L || l > R || l > r) return;
int mid = l + r >> 1;
if(L <= l && r <= R) sum[i] += k;
else {
if(L <= mid) modify(i << 1, l, mid, L, R, k, e);
if(R > mid) modify(i << 1 | 1, mid + 1, r, L, R, k, e);
}
if(sum[i]) len[i] = e[r + 1] - e[l];
else len[i] = len[i << 1] + len[i << 1 | 1];
}
}T, T1;
unordered_map<int, int> mp, mp1;
int main() {
cin >> n;
rep(i, 1, n) {
int x1, x2, y1, y2;
cin >> x1 >> y1 >> x2 >> y2;
a[++cnt] = {x1, y1, y2, 1};
a[++cnt] = {x2, y1, y2, -1};
c[++cnt1] = {y1, x1, x2, 1};
c[++cnt1] = {y2, x1, x2, -1};
b[++tot] = y1, b[++tot] = y2;
d[++tot1] = x1, d[++tot1] = x2;
}
sort(a + 1, a + cnt + 1);
sort(b + 1, b + tot + 1);
tot = unique(b + 1, b + tot + 1) - b - 1;
rep(i, 1, tot) mp[b[i]] = i;
rep(i, 1, cnt) {
T.modify(1, 1, tot + 1, mp[a[i].y1], mp[a[i].y2] - 1, a[i].type, b);
ans += abs(T.len[1] - lst);
lst = T.len[1];
}
sort(c + 1, c + cnt1 + 1);
sort(d + 1, d + tot1 + 1);
tot1 = unique(d + 1, d + tot1 + 1) - d - 1;
rep(i, 1, tot1) mp1[d[i]] = i;
lst = 0;
rep(i, 1, cnt1) {
T1.modify(1, 1, tot1 + 1, mp1[c[i].x1], mp1[c[i].x2] - 1, c[i].type, d);
ans += abs(T1.len[1] - lst);//加个变化量就行
lst = T1.len[1];
}
cout << ans;
return 0;
}
没啥好解释的。不过,如果你 56 pts,请注意我们在一开始对边排序的时候要这么写,也就是先加后减。
if(x ^ rhs.x) {
return x < rhs.x;
}
return type > rhs.type;
为啥捏?
如果你先减,那么如果给一部分减没了,那么在加的时候就会判断成你新增的变化量。这不就加多了吗。

浙公网安备 33010602011771号