P1904. 天际线

考点

  • 扫描线

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e4 + 50;
int n, ltot;

struct node1 {
int x_, y1_, y2_, flag_;
bool operator<(node1 &x) {
if (x_ != x.x_) return x_ < x.x_;
return flag_ > x.flag_;
}
} line[2 * maxn];

struct node2 {
int sum_, status_;
} tree[4 * 2 * maxn];
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define sum(x) tree[x].sum_
#define stat(x) tree[x].status_

void up(int rt, int l, int r) {
sum(rt) = stat(rt) ? r + 1 - l : (l == r ? 0 : sum(lson) + sum(rson));
}

void update(int rt, int l, int r, int L, int R, int v) {
if (L <= l && r <= R) {
stat(rt) += v, up(rt, l, r);
return;
}
int mid = (l + r) / 2;
if (L <= mid) update(lson, l, mid, L, R, v);
if (R > mid) update(rson, mid + 1, r, L, R, v);
up(rt, l, r);
}

int main() {
int x1, x2, h;
while (cin >> x1 >> h >> x2) {
line[++ltot] = {x1, 0, h, 1}, line[++ltot] = {x2, 0, h, -1};
}
sort(line + 1, line + 1 + ltot);
int pre_sum = -1;
for (int i = 1; i <= ltot; ++i) {
update(1, 0, maxn, line[i].y1_, line[i].y2_ - 1, line[i].flag_);
if (sum(1) == pre_sum) continue;
cout << line[i].x_ << " " << sum(1) << " ";
pre_sum = sum(1);
}
return 0;
}

思路

矩形相交的问题考虑使用扫描线。

发现规律,若当前扫描线的长度不等于上一次扫描线的长度,输出当前的x坐标和扫描线长度即可。

注意!若有重合扫描线(即同一x坐标),应先加后减!先减后加会导致不必要的长度波动。

比如当前x坐标上,有{+1, +1, -1}三根扫描线,且长度均相等。

若先增后减,则变化为+1 -> +1 -> +1,是正确的;

若先减后增,则变化为-1 -> 0 -> +1,尽管结果相同,但中途多出了两次变化,是错误的。