• [英雄星球六月集训LeetCode解题日报] 第24日 线段树


    日报

    题目

    一、731. 我的日程安排表 II

    731. 我的日程安排表 II

    1. 题目描述

    1. 我的日程安排表 II

    难度:中等

    实现一个 MyCalendar 类来存放你的日程安排。如果要添加的时间内不会导致三重预订时,则可以存储这个新的日程安排。

    MyCalendar 有一个 book(int start, int end)方法。它意味着在 startend 时间内增加一个日程安排,注意,这里的时间是半开区间,即 [start, end), 实数 x 的范围为, start <= x < end

    当三个日程安排有一些时间上的交叉时(例如三个日程安排都在同一时间内),就会产生三重预订。

    每次调用 MyCalendar.book方法时,如果可以将日程安排成功添加到日历中而不会导致三重预订,返回 true。否则,返回 false 并且不要将该日程安排添加到日历中。

    请按照以下步骤调用MyCalendar 类: MyCalendar cal = new MyCalendar(); MyCalendar.book(start, end)

    示例:

    MyCalendar();
    MyCalendar.book(10, 20); // returns true
    MyCalendar.book(50, 60); // returns true
    MyCalendar.book(10, 40); // returns true
    MyCalendar.book(5, 15); // returns false
    MyCalendar.book(5, 10); // returns true
    MyCalendar.book(25, 55); // returns true
    解释: 
    前两个日程安排可以添加至日历中。 第三个日程安排会导致双重预订,但可以添加至日历中。
    第四个日程安排活动(5,15)不能添加至日历中,因为它会导致三重预订。
    第五个日程安排(5,10)可以添加至日历中,因为它未使用已经双重预订的时间10。
    第六个日程安排(25,55)可以添加至日历中,因为时间 [25,40] 将和第三个日程安排双重预订;
    时间 [40,50] 将单独预订,时间 [50,55)将和第二个日程安排双重预订。
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    提示:

    • 每个测试用例,调用 MyCalendar.book 函数最多不超过 1000次。
    • 调用函数 MyCalendar.book(start, end)时, startend 的取值范围为 [0, 10^9]

    2. 思路分析

    • 维护区间[l,r]时间区间上的预定数。
    • 发现当前区间最大值为2了,则这个区间再插入就是3.
    • 显然是线段树IUIQ的板子,区间更新就是要考虑Lazytag。
    • 发现数据范围10^9,那么考虑离线做离散化,发现强行禁止离线,只能在线做。
    • 那么找到动态开点线段树的板子,CV成功!
    • 这里说一下废话:为什么要用线段树或者珂朵莉而不能用数组模拟:因为数组模拟是 O(nm) 的,n是每次线段平均长度,这里最大是 10^9 。肯定过不了。
    • 而线段树可以把这个过程变成 O(mlgn)

    3. 代码实现

    class IntervalTree:
        def __init__(self):
            self.interval_tree = collections.defaultdict(int)
            self.lazys = collections.defaultdict(int)
            
    
        def give_lay_to_son(self,p,l,r):
            interval_tree = self.interval_tree
            lazys = self.lazys
            if lazys[p] == 0:
                return
            mid = (l+r)//2
            interval_tree[p*2] += lazys[p]
            interval_tree[p*2+1] += lazys[p]
            lazys[p*2] += lazys[p]
            lazys[p*2+1] += lazys[p]
            lazys[p] = 0
            
        def add(self,p,l,r,x,y,val):
            """
            把[x,y]区域全+val
            """
            if r < x or y < l:  # 这里不加就会TLE
                return 
            interval_tree = self.interval_tree    
            lazys = self.lazys        
            if x <= l and r<=y:
                interval_tree[p] += val
                lazys[p] += val
                return
            self.give_lay_to_son(p,l,r)  
            mid = (l+r)//2
            if x <= mid:
                self.add(p*2,l,mid,x,y,val)
            if mid < y:
                self.add(p*2+1,mid+1,r,x,y,val)
            interval_tree[p] = max(interval_tree[p*2], interval_tree[p*2+1])
        
        def query(self,p,l,r,x,y):
            """
            查找x,y区间的最大值
            """        
            if y < l or r < x:
                return 0
            if x<=l and r<=y:
                return self.interval_tree[p]
            self.give_lay_to_son(p,l,r)
            mid = (l+r)//2
            s = 0
            if x <= mid:
                s = max(s,self.query(p*2,l,mid,x,y))
            if mid < y:
                s = max(s,self.query(p*2+1,mid+1,r,x,y))
            return s
    
    class MyCalendarTwo:
    
        def __init__(self):
            self.tree = IntervalTree()
    
    
        def book(self, start: int, end: int) -> bool:
            m = self.tree.query(1,1,10**9+1,start+1,end)
            if m >= 2:
                return False
    
            self.tree.add(1,1,10**9+1,start+1,end,1) 
            return True
    
    • 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

    二、 699. 掉落的方块

    链接: 699. 掉落的方块

    1. 题目描述

    1. 掉落的方块

    难度:困难

    在二维平面上的 x 轴上,放置着一些方块。

    给你一个二维整数数组 positions ,其中 positions[i] = [lefti, sideLengthi] 表示:第 i 个方块边长为 sideLengthi ,其左侧边与 x 轴上坐标点 lefti 对齐。

    每个方块都从一个比目前所有的落地方块更高的高度掉落而下。方块沿 y 轴负方向下落,直到着陆到 另一个正方形的顶边 或者是 x 轴上 。一个方块仅仅是擦过另一个方块的左侧边或右侧边不算着陆。一旦着陆,它就会固定在原地,无法移动。

    在每个方块掉落后,你必须记录目前所有已经落稳的 方块堆叠的最高高度

    返回一个整数数组 ans ,其中 ans[i] 表示在第 i 块方块掉落后堆叠的最高高度。

    示例 1:

    输入:positions = [[1,2],[2,3],[6,1]]
    输出:[2,5,5]
    解释:
    第 1 个方块掉落后,最高的堆叠由方块 1 组成,堆叠的最高高度为 2 。
    第 2 个方块掉落后,最高的堆叠由方块 1 和 2 组成,堆叠的最高高度为 5 。
    第 3 个方块掉落后,最高的堆叠仍然由方块 1 和 2 组成,堆叠的最高高度为 5 。
    因此,返回 [2, 5, 5] 作为答案。
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    示例 2:

    输入:positions = [[100,100],[200,100]]
    输出:[100,100]
    解释:
    第 1 个方块掉落后,最高的堆叠由方块 1 组成,堆叠的最高高度为 100 。
    第 2 个方块掉落后,最高的堆叠可以由方块 1 组成也可以由方块 2 组成,堆叠的最高高度为 100 。
    因此,返回 [100, 100] 作为答案。
    注意,方块 2 擦过方块 1 的右侧边,但不会算作在方块 1 上着陆。
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    提示:

    • 1 <= positions.length <= 1000
    • 1 <= lefti <= 108
    • 1 <= sideLengthi <= 106

    2. 思路分析

    • 方块掉落时,显然高度取决于这个方块底边管辖内,当前最高的方块,本方块会落在上边。
    • 那我们需要的是一个快速查询区间极值,快速区间赋值的数据结构,显然线段树可以。
    • 这题范围较大,但是可以离线,那就离散化吧。
    • 这题有大量区间推平操作,可以珂朵莉。
    • 这里还是贴一个线段树,需要珂朵莉可以去我上边贴的地址看。

    3. 代码实现

    class IntervalTree:
        def __init__(self, size):
            self.size = size
            self.interval_tree = [0 for _ in range(size*4)]
            self.lazys = [0 for _ in range(size*4)]
    
        def give_lay_to_son(self,p,l,r):
            interval_tree = self.interval_tree
            lazys = self.lazys
            if lazys[p] == 0:
                return
            mid = (l+r)//2
            interval_tree[p*2] = lazys[p]
            interval_tree[p*2+1] = lazys[p]
            lazys[p*2] = lazys[p]
            lazys[p*2+1] = lazys[p]
            lazys[p] = 0
            
        def update(self,p,l,r,x,y,val):
            """
            把[x,y]区域全变成val
            """
            if y < l or r < x:
                return 
            interval_tree = self.interval_tree    
            lazys = self.lazys        
            if x <= l and r<=y:
                interval_tree[p] = val
                lazys[p] = val
                return
            self.give_lay_to_son(p,l,r)
            mid = (l+r)//2
            if x <= mid:
                self.update(p*2,l,mid,x,y,val)
            if mid < y:
                self.update(p*2+1,mid+1,r,x,y,val)
            interval_tree[p] = max(interval_tree[p*2], interval_tree[p*2+1])
        
        def query(self,p,l,r,x,y):
            """
            查找x,y区间的最大值        """        
            
            if y < l or r < x:
                return 0
            if x<=l and r<=y:
                return self.interval_tree[p]
            self.give_lay_to_son(p,l,r)
            mid = (l+r)//2
            s = 0
            if x <= mid:
                s = max(s,self.query(p*2,l,mid,x,y))
            if mid < y:
                s = max(s,self.query(p*2+1,mid+1,r,x,y))
            return s
    
    class Solution:
        def fallingSquares(self, positions: List[List[int]]) -> List[int]:
            n = len(positions)
            hashes = [left for left,_ in positions] + [left+side for left,side in positions] 
            hashes = sorted(list(set(hashes)))
            # 用线段树维护x轴区间最大值,记录每个点的高度:比如[1,2]这个方块,会使线段[1,2]闭区间这个线段上的每个高度都变成2
            # 落下一个新方块时,查询它的底边所在线段[x,y]的最大高度h,这个方块会落在这个高度h,把新高度h+side插入线段树[x,y]的部分
            # 每次插入结束,树根存的高度就是当前最大高度
            # 由于数据范围大1 <= lefti <= 108,需要散列化
            # 散列化的值有left和right(线段短点)
            # print(hashes)
            tree_size = len(hashes)
            tree = IntervalTree(tree_size)
            heights = []
            for left,d in positions:
                right = left + d 
                l = bisect_left(hashes,left)
                r = bisect_left(hashes,right)
                h = tree.query(1,1,tree_size,l+1,r)
                tree.update(1,1,tree_size,l+1,r,h+d)
                heights.append(tree.interval_tree[1])
            return heights
    
    • 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

    人生苦短,我用Python!

  • 相关阅读:
    数据结构之队列
    【python入门专项练习】-N06.循环语句
    助力交叉学科应用型数据科学人才培养,和鲸科技携手华为发布联合解决方案
    现在的湖仓一体像是个伪命题
    【吴恩达机器学习笔记】十四、推荐系统
    vue使用echarts图表
    如何使用ArcGIS Pro提取河网水系
    DeepStream--测试TrafficCamNet检测模型
    Oracle EBS R12.1 FA 批量计划外折旧
    汽车控制器软件正向开发
  • 原文地址:https://blog.csdn.net/liuliangcan/article/details/125438323