我的问题起源:灵佬关于293周赛最后一题的题解
使用范围: 区间合并,当数据量大到差分数组之类的常规解法无能为力的时候
问题描述: H2276
简单来说就是合并区间,并计算区间内数的个数
上题题解的解释:
使用有序字典(SortedDict)存储区间的结束和开始(注意是反的!key是区间的结束,value是区间的开始)。当有新的区间输入时,从头到尾一个个的遍历字典(为了节省时间,一般采用二分直接找到第一个>=传入left的地方再开始遍历),只要有重复的部分就将这个区间合并(其实就是删除这个区间并更新left和right)。
具体题解如下:
class CountIntervals:
def __init__(self):
self.d = SortedDict()
self.cnt = 0 # 所有区间长度和
def add(self, left: int, right: int) -> None:
# 遍历所有被 [left,right] 覆盖到的区间(部分覆盖也算)
# bisect_left(left)返回第一个>=left 的数的索引
i = self.d.bisect_left(left)
while i < len(self.d) and self.d.values()[i] <= right:
r, l = self.d.items()[i]
left = min(left, l) # 合并后的新区间,其左端点为所有被覆盖的区间的左端点的最小值
right = max(right, r) # 合并后的新区间,其右端点为所有被覆盖的区间的右端点的最大值
self.cnt -= r - l + 1
self.d.popitem(i)
self.cnt += right - left + 1
self.d[right] = left # 所有被覆盖到的区间与 [left,right] 合并成一个新区间
def count(self) -> int:
return self.cnt
函数套用版本:
from sortedcontainers import SortedDict
all_intervals = SortedDict()
nums_n = 0
def add(interval: list):
left = interval[0]
right = interval[1]
i = all_intervals.bisect_left(left)
while i < len(all_intervals) and all_intervals.values()[i] <= right:
r, l = all_intervals.items()[i]
left = min(l, left)
right = max(r, right)
# 这里用了nonlocal记录总的数的个数,在解题时才会有效
nonlocal nums_n
nums_n -= r-l+1
all_intervals.popitem(i)
# 注意,i并没有更新,这是因为如果上面的操作成功进行,第i个值会被删除,直接用
# 新的left和right去判断下一和区间,直到进while的条件不满足为止
nonlocal nums_n
nums_n += right-left+1
all_intervals[right] = left