【洛谷】P1904 天际线

题目地址:

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,r1)这一段的每一个数 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)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值