难度:中等
实现一个 MyCalendar
类来存放你的日程安排。如果要添加的时间内不会导致三重预订时,则可以存储这个新的日程安排。
MyCalendar
有一个 book(int start, int end)
方法。它意味着在 start
到 end
时间内增加一个日程安排,注意,这里的时间是半开区间,即 [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)将和第二个日程安排双重预订。
提示:
MyCalendar.book
函数最多不超过 1000
次。MyCalendar.book(start, end)
时, start
和 end
的取值范围为 [0, 10^9]
。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
链接: 699. 掉落的方块
难度:困难
在二维平面上的 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] 作为答案。
示例 2:
输入:positions = [[100,100],[200,100]]
输出:[100,100]
解释:
第 1 个方块掉落后,最高的堆叠由方块 1 组成,堆叠的最高高度为 100 。
第 2 个方块掉落后,最高的堆叠可以由方块 1 组成也可以由方块 2 组成,堆叠的最高高度为 100 。
因此,返回 [100, 100] 作为答案。
注意,方块 2 擦过方块 1 的右侧边,但不会算作在方块 1 上着陆。
提示:
1 <= positions.length <= 1000
1 <= lefti <= 108
1 <= sideLengthi <= 106
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
人生苦短,我用Python!