题目地址:
https://www.luogu.com.cn/problem/P1904
题目描述:
Latium省的Genoa是亚平宁半岛西海岸北端的一片土地,自然资源丰富,却无人居住。你受到罗马执政官Caesar的委任,前往Genoa建立新的城市。Caesar对这次任务的要求是在Genoa这片土地上建立起一座繁荣的城市,他将以此作为衡量你的表现的标准。
正在你大刀阔斧地进行城市建设的时候,Caesar突然写信给你,说他要检查Genoa的建设情况。Caesar希望知道你的城市是什么样子,但是他又非常的忙,所以他只要你描述一下城市的轮廓就可以了,他将依照城市的轮廓决定你的薪水。
怎样描述一个城市的轮廓呢?我们知道Genoa所有的建筑共享一个地面,你可以认为它是水平的。所有的建筑用一个三元组
(
L
i
,
H
i
,
R
i
)
(L_i,H_i,R_i)
(Li,Hi,Ri),其中
L
i
L_i
Li和
R
i
R_i
Ri分别是建筑的左坐标和右坐标,
H
i
H_i
Hi就是建筑的高度。在下方所示的图表中左边建筑物描述如下
(
1
,
11
,
5
)
(1,11,5)
(1,11,5),
(
2
,
6
,
7
)
(2,6,7)
(2,6,7),
(
3
,
13
,
9
)
(3,13,9)
(3,13,9),
(
12
,
7
,
16
)
(12,7,16)
(12,7,16),
(
14
,
3
,
25
)
(14,3,25)
(14,3,25),
(
19
,
18
,
22
)
(19,18,22)
(19,18,22),
(
23
,
13
,
29
)
(23,13,29)
(23,13,29),
(
24
,
4
,
28
)
(24,4,28)
(24,4,28),右边用轮廓线的顺序
(
1
,
11
,
3
,
13
,
9
,
0
,
12
,
7
,
16
,
3
,
19
,
18
,
22
,
3
,
23
,
13
,
29
,
0
)
(1,11,3,13,9,0,12,7,16,3,19,18,22,3,23,13,29,0)
(1,11,3,13,9,0,12,7,16,3,19,18,22,3,23,13,29,0)表示:
输入格式:
在输入数据中,你将得到一系列表示建筑的三元组。在输入数据中所有建筑的坐标中的数值都是小于
10000
10000
10000的正整数,且至少有
1
1
1幢建筑,最多有
5000
5000
5000幢建筑。在输入输入中每幢建筑的三元组各占一行。三元组中的所有整数应由一个或多个空格分开。
输出格式:
在输出数据中,你被要求给出城市的轮廓线。你可以这样来描述:对于所有轮廓线上的折点,按顺序排好,第奇数个点输出
x
x
x坐标,第偶数个点输出
y
y
y坐标,两个数之间用空格分开。
可以用分块来做(当然也可以用线段树,思路类似。可以参考https://blog.youkuaiyun.com/qq_46105170/article/details/121259936)。思路是,将地平线视为一个数组,一开始全为 0 0 0,每个建筑 ( l , h , r ) (l,h,r) (l,h,r)可以视为一个修改,将该数组的 ( l , r − 1 ) (l,r-1) (l,r−1)这一段的每一个数 x x x变为 max { x , h } \max\{x,h\} max{x,h}。这一点可以用分块来做,每次操作时间 O ( n ) O(\sqrt n) O(n)。最后只需要将整个数组还原回来,找到转折点即可。代码如下:
#include <cmath>
#include <iostream>
using namespace std;
const int N = 1e4 + 10, M = 500;
int n, m;
int w[N], bel[N];
int len;
int height[M];
struct Query {
int l, r, h;
} q[N];
void modify(int l, int r, int h) {
int bl = bel[l], br = bel[r];
if (bl == br)
for (int i = l; i <= r; i++) w[i] = max(w[i], h);
else {
int i = l, j = r;
while (bel[i] == bl) w[i] = max(w[i], h), i++;
while (bel[j] == br) w[j] = max(w[j], h), j--;
for (int k = bel[i]; k <= bel[j]; k++) height[k] = max(height[k], h);
}
}
int main() {
int l, r, h;
while (cin >> l >> h >> r) {
q[++m] = {l, r - 1, h};
n = max(n, q[m].r);
}
n++;
len = sqrt(n);
for (int i = 1; i <= n; i++) bel[i] = i / len;
for (int i = 1; i <= m; i++) modify(q[i].l, q[i].r, q[i].h);
int y = 0;
for (int i = 1; i <= n; i++)
if (max(w[i], height[bel[i]]) != y) {
y = max(w[i], height[bel[i]]);
printf("%d %d ", i, y);
}
}
时间复杂度 O ( m n + n ) O(m\sqrt n+n) O(mn+n), n n n为建筑的左右点范围, m m m为建筑个数,空间 O ( n ) O(n) O(n)。