题目

https://leetcode-cn.com/problems/the-skyline-problem/

题解

线段树问题,根据左神的思路改编,外加我想到的压缩的 tricks(数字范围太大,实在想不出别的办法了,一开始是直接把过大的数除以某个 scale,但是这样是有损的,无法复原,于是想到了用 map 存压缩前后的映射关系。只要保持压缩前后的数字大小顺序不变,这个压缩就是无损的。)

class Solution {public static final int N = 1 << 15; // 2^15=32768,考虑到 buildings.length <= 10^4,压缩后总共不超过2*10^4个横坐标public List<List<Integer>> getSkyline(int[][] buildings) {SegmentTree segmentTree = new SegmentTree(N);// 压缩(编码)// 排序,然后映射,因为不超过10000,所以肯定能映射开Set<Integer> set = new TreeSet<>();for (int[] task : buildings) {set.add(task[0]);set.add(task[1]);}Map<Integer, Integer> decodeMap = new HashMap<>();Map<Integer, Integer> encodeMap = new HashMap<>();int cipher = 0;for (int plain : set) {cipher += 2; // 用+2而不是+1,目的是留出地面的间隔,避免两个楼挨在一起时,丢失了地面decodeMap.put(cipher, plain);encodeMap.put(plain, cipher);}for (int[] task : buildings) {task[0] = encodeMap.get(task[0]);task[1] = encodeMap.get(task[1]);}// 更新线段树(核心)for (int[] task : buildings) {segmentTree.update(task[0], task[1], task[2], 0, N, 1); // 把从task[0]到task[1]的位置更新为task[2]}segmentTree.flush();// 打补丁(之前为了方便下标索引,线段树是从1位置开始构建的,现在要把0位置也考虑进去)segmentTree.max[N - 2] = 0;segmentTree.max[2 * N - 1] = 0;for (int[] task : buildings) {if (task[0] == 0) segmentTree.max[N - 1] = Math.max(segmentTree.max[N - 1], task[2]);}// 构造返回值(遍历所有叶子)List<List<Integer>> result = new ArrayList<>();for (int i = N - 1; i < 2 * N; i++) {if (segmentTree.max[i] != segmentTree.max[i - 1]) {ArrayList<Integer> list = new ArrayList<>();list.add(segmentTree.max[i - 1] < segmentTree.max[i] ? i - N + 1 : i - N);list.add(segmentTree.max[i]);result.add(list);}}// 解码for (List<Integer> list : result) {list.set(0, decodeMap.get(list.get(0)));}return result;}public static class SegmentTree {private int SIZE;private int[] max;private int[] change;private boolean[] update;public SegmentTree(int N) {SIZE = N + 1;max = new int[SIZE << 2]; // 用来支持脑补概念中,某一个范围的累加和信息change = new int[SIZE << 2]; // 用来支持脑补概念中,某一个范围有没有更新操作的任务update = new boolean[SIZE << 2]; // 用来支持脑补概念中,某一个范围是否需要更新(而不是更新成0)}// 之前的所有懒增加、懒更新,从父范围发给左右两个子范围,分发策略是什么private void pushDown(int rt) {if (update[rt]) {update[rt << 1] = true;update[rt << 1 | 1] = true;change[rt << 1] = change[rt];change[rt << 1 | 1] = change[rt];max[rt << 1] = Math.max(max[rt << 1], change[rt]);max[rt << 1 | 1] = Math.max(max[rt << 1 | 1], change[rt]);update[rt] = false;}}// 想要把 L~R 所有的值变成 H// 当前影响 l~r,当前范围信息存在数组的 rt 位置public void update(int L, int R, int H, int l, int r, int rt) {if (L <= l && r <= R) {if (H >= max[rt]) {update[rt] = true;change[rt] = H;max[rt] = H;}return;}// 当前任务躲不掉,无法懒更新,要往下发一层int mid = (l + r) >> 1;pushDown(rt);if (L <= mid) {update(L, R, H, l, mid, rt << 1);}if (R > mid) {update(L, R, H, mid + 1, r, rt << 1 | 1);}}// 把所有的懒更新都下放到底部,类似于自底向上的堆排序public void flush() {for (int i = max.length - 1; i >= 0; i--) {if (max[i] == 0) heapify(i);}}public int heapify(int i) {if (i == 0) return 0;max[i] = Math.max(max[i], heapify(i / 2));return max[i];}}
}

leetcode 218. The Skyline Problem | 218. 天际线问题(线段树)相关推荐

  1. 【BZOJ】3339: Rmq Problem 3585: mex(线段树+特殊的技巧)

    http://www.lydsy.com/JudgeOnline/problem.php?id=3585 好神的题. 但是!!!!!!!!!!!!!!我线段树现在要开8倍空间才能过!!!!!!!!!! ...

  2. Problem G. Pandaria(线段树合并 + Kruskal 重构树)

    Problem G. Pandaria 给定一个有nnn条边的无向连通图,每条边有对应的边权,每个点有一个颜色, 问从一个点出发,经过不超过www的边权,所能到达的点中,颜色出现次数做多且颜色编号最小 ...

  3. LeetCode 218. The Skyline Problem

    题目地址: 注意:做这道题前建议先做这道题Rectangle Area II - LeetCode 看这篇博客:LeetCode 850. Rectangle Area II A city's sky ...

  4. UOJ#218. 【UNR #1】火车管理 线段树 主席树

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ218.html 题解 如果我们可以知道每次弹出栈之后新的栈顶是什么,那么我们就可以在一棵区间覆盖.区间求和 ...

  5. POJ3468 A Simple Problem with Integers【线段树 成段更新+求和 lazy标志】

    用longlong替换__int64也成. #define LL long long 输入输出用%lld Problem: 3468   User: qq1203456195 Memory: 4284 ...

  6. POJ 3468 A Simple Problem with Integers(线段树:区间更新)

    http://poj.org/problem?id=3468 题意: 给出一串数,每次在一个区间内增加c,查询[a,b]时输出a.b之间的总和. 思路: 总结一下懒惰标记的用法吧. 比如要对一个区间范 ...

  7. POJ3468-A Simple Problem with Integers【线段树,树状数组,分块】

    正题 题目链接:我是链接 其实洛谷线段树模板也是一样的:三种方法AC评测链接 题目大意 要求支持区间修改,区间求和. 线段树 直接用一个lazy标记,在之前的博客里有说 code1 #include& ...

  8. 【POJ - 3468 】 A Simple Problem with Integers (线段树模板 区间更新 + 区间和查询)(不能树状数组或差分数组)

    题干: You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type o ...

  9. F - A Simple Problem with Integers(线段树)

    Description 给出了一个序列,你需要处理如下两种询问. "C a b c"表示给[a, b]区间中的值全部增加c (-10000 ≤ c ≤ 10000). " ...

最新文章

  1. 170亿参数加持,微软发布史上最大Transformer模型
  2. 机器学习_生成式模型与判别式模型
  3. 稀疏矩阵按列转置核心代码
  4. swift4.0-11 类和结构体
  5. 数据结构与算法17-表插入排序
  6. 中科院遥感与数字地球研究所博士后招聘
  7. 一文详解最常用的10个「激活函数」
  8. springboot整合通用mapper操作数据库
  9. 改善CSS的10种最佳做法,帮助你从样式中获得最大的收益
  10. OpenCV基本线条操作
  11. Mac使用OpenCV项目步骤
  12. Git版本控制:Github的使用之 多人协作及参与项目
  13. 简要概述网络I/O与并发
  14. nvidia控制面板官方版-nvidia控制面板附安装教程
  15. go 并发编程之-工作池
  16. Android控件—Toast(吐司)
  17. 免校准的电量计量芯片_【应用】基于高精度免校准电能计量芯片CSE7761的漏电保护设计,可支持单芯片两路计量...
  18. 2、GIT---时光穿梭机
  19. Unity获取物体自身坐标轴的方向以及沿着该方向运动的方法
  20. 服务通知——小程序消息推送、模板消息推送demo

热门文章

  1. HDU - 4253 Two Famous Companies(二分+最小生成树)
  2. CodeForces - 1301C Ayoub's function(数学)
  3. (转)二维平面坐标系-最近点对模板
  4. Windows下Anaconda3安装及使用教程
  5. php时间到期提醒功能,php还剩多长时间过期函数
  6. L1-038. 新世界
  7. 利用小工具instsrv和srvany 创建windows服务
  8. cocos2d-x游戏开发(十一)细说场景切换
  9. STL中list用法详解
  10. 关于WM_NCHITTEST消息