hdoj-4052 Adding New Machine

考点

  • 扫描线

题解

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e4 + 50;
int w, h, n, m, ytot, ltot, dy[2 * maxn];

struct node1 {
int x1_, y1_, x2_, y2_;
} arr[maxn];

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

struct node3 {
ll cnt_, sum_;
} seg[4 * 2 * maxn];
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define cnt(x) seg[x].cnt_
#define sum(x) seg[x].sum_

void up(int rt, int l, int r) {
sum(rt) = cnt(rt) ? dy[r + 1] - dy[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) {
cnt(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);
}

// x方向
ll xwork() {
ll area = 0;
ltot = ytot = 0, memset(seg, 0, sizeof(seg));
for (int i = 1; i <= n; ++i) {
line[++ltot] = {max(0, arr[i].x1_ - m + 1), arr[i].y1_, arr[i].y2_, 1};
line[++ltot] = {arr[i].x2_, arr[i].y1_, arr[i].y2_, -1};
dy[++ytot] = arr[i].y1_, dy[++ytot] = arr[i].y2_;
}
line[++ltot] = {max(0, w - m + 1), 0, h, 1};
line[++ltot] = {w, 0, h, -1};
dy[++ytot] = 0, dy[++ytot] = h;
sort(dy + 1, dy + 1 + ytot), sort(line + 1, line + 1 + ltot);
ytot = unique(dy + 1, dy + 1 + ytot) - 1 - dy;
for (int i = 1; i < ltot; ++i) {
int y1 = lower_bound(dy + 1, dy + 1 + ytot, line[i].y1_) - dy;
int y2 = lower_bound(dy + 1, dy + 1 + ytot, line[i].y2_) - dy;
update(1, 1, ytot - 1, y1, y2 - 1, line[i].flag_);
area += (line[i + 1].x_ - line[i].x_) * sum(1);
}
return area;
}

// y方向
ll ywork() {
ll area = 0;
ltot = ytot = 0, memset(seg, 0, sizeof(seg));
for (int i = 1; i <= n; ++i) {
line[++ltot] = {max(0, arr[i].y1_ - m + 1), arr[i].x1_, arr[i].x2_, 1};
line[++ltot] = {arr[i].y2_, arr[i].x1_, arr[i].x2_, -1};
dy[++ytot] = arr[i].x1_, dy[++ytot] = arr[i].x2_;
}
line[++ltot] = {max(0, h - m + 1), 0, w, 1};
line[++ltot] = {h, 0, w, -1};
dy[++ytot] = 0, dy[++ytot] = w;
sort(dy + 1, dy + 1 + ytot), sort(line + 1, line + 1 + ltot);
ytot = unique(dy + 1, dy + 1 + ytot) - 1 - dy;
for (int i = 1; i < ltot; ++i) {
int y1 = lower_bound(dy + 1, dy + 1 + ytot, line[i].y1_) - dy;
int y2 = lower_bound(dy + 1, dy + 1 + ytot, line[i].y2_) - dy;
update(1, 1, ytot - 1, y1, y2 - 1, line[i].flag_);
area += (line[i + 1].x_ - line[i].x_) * sum(1);
}
return area;
}

int main() {
while (cin >> w >> h >> n >> m) {
ll res = 0;
for (int i = 1; i <= n; ++i) {
cin >> arr[i].x1_ >> arr[i].y1_ >> arr[i].x2_ >> arr[i].y2_;
--arr[i].x1_, --arr[i].y1_;
}
if (w >= m) res += 1ll * w * h - xwork();
// m==1时,x方向和y方向的情况是一样的,不能重复计算!
if (h >= m && m != 1) res += 1ll * w * h - ywork();
cout << res << endl;
}
return 0;
}

思路

wh的范围高达1e7,而矩形个数只有5e4,显然是暗示用扫描线来对矩形做文章。

扫描线需要用到的是矩形的左下角坐标和右上角坐标,

而题目所给的是左下角单位矩形的二维标号和右上角单位矩形的二维标号,

比如样例:

3 3 1 2 2 2 2 2

左下角矩形是(2,2),右上角矩形是(2,2);那么左下角坐标应该是(1,1),右上角坐标不变。

由于可以横着放或竖着放,显然有总情况 = 横放情况 + 竖放情况,先只考虑横放。

只考虑横放时,样例作图如下。被覆盖面积是不可选的,而可选的空白部分 = 总面积 - 被覆盖面积

而被覆盖面积除了题目所给的矩形面积并之外,还有左下角(max(0, w - m + 1),0),右上角(w,h)构成的矩形。

矩形面积并是扫描线的经典模板,不再赘述;竖着放的情况与横着放类似,也不再赘述。

提示一点,当m == 1时,横放竖放情况一致,不要重复计算。