执行结果:通过

题目:3244 新增道路查询后的最短距离II
给你一个整数 n 和一个二维整数数组 queries。
有 n 个城市,编号从 0 到 n - 1。初始时,每个城市 i 都有一条单向道路通往城市 i + 1( 0 <= i < n - 1)。
queries[i] = [ui, vi] 表示新建一条从城市 ui 到城市 vi 的单向道路。每次查询后,你需要找到从城市 0 到城市 n - 1 的最短路径的长度。
所有查询中不会存在两个查询都满足 queries[i][0] < queries[j][0] < queries[i][1] < queries[j][1]。
返回一个数组 answer,对于范围 [0, queries.length - 1] 中的每个 i,answer[i] 是处理完前 i + 1 个查询后,从城市 0 到城市 n - 1 的最短路径的长度。
示例 1:
输入: n = 5, queries = [[2, 4], [0, 2], [0, 4]]
输出: [3, 2, 1]
解释:

新增一条从 2 到 4 的道路后,从 0 到 4 的最短路径长度为 3。

新增一条从 0 到 2 的道路后,从 0 到 4 的最短路径长度为 2。

新增一条从 0 到 4 的道路后,从 0 到 4 的最短路径长度为 1。
示例 2:
输入: n = 4, queries = [[0, 3], [0, 2]]
输出: [1, 1]
解释:

新增一条从 0 到 3 的道路后,从 0 到 3 的最短路径长度为 1。

新增一条从 0 到 2 的道路后,从 0 到 3 的最短路径长度仍为 1。

提示:
3 <= n <= 1051 <= queries.length <= 105queries[i].length == 20 <= queries[i][0] < queries[i][1] < n1 < queries[i][1] - queries[i][0]- 查询中不存在重复的道路。
- 不存在两个查询都满足
i != j且queries[i][0] < queries[j][0] < queries[i][1] < queries[j][1]。
代码以及解题思路
代码:
class Solution:
def shortestDistanceAfterQueries(self, n: int, queries: List[List[int]]) -> List[int]:
st = LazySegmentTree(n)
ans = []
for l, r in queries:
st.update(1,1,n,l+2,r, 0)
ans.append(st.cnt[1]-1)
return ans
class LazySegmentTree:
def __init__(self, n: int):
self.cnt = [0] * (4 * n)
self.todo = [-1] * (4 * n)
self.build(1,1,n)
# 初始化线段树 o,l,r=1,1,n
def build(self, o: int, l: int, r: int) -> None:
if l == r:
self.cnt[o] = 1
return
m = (l + r) >> 1
self.build(o * 2, l, m)
self.build(o * 2 + 1, m + 1, r)
self.maintain(o)
def maintain(self, o: int) -> None:
self.cnt[o] = self.cnt[o * 2] + self.cnt[o * 2 + 1]
def do(self, o: int, val: int) -> None:
self.cnt[o] = val
self.todo[o] = val
def spread(self, o: int) -> None:
v = self.todo[o]
if v == 0:
self.do(o * 2, v)
self.do(o * 2 + 1, v)
self.todo[o] = -1
def update(self, o: int, l: int, r: int, L: int, R: int, val: int) -> None:
if L <= l and r <= R:
self.do(o, val)
return
self.spread(o)
m = (l + r) >> 1
if m >= L:
self.update(o * 2, l, m, L, R, val)
if m < R:
self.update(o * 2 + 1, m + 1, r, L, R, val)
self.maintain(o)
def query(self, o: int, l: int, r: int, L: int, R: int) -> int:
if L <= l and r <= R:
return self.cnt[o]
self.spread(o)
m = (l + r) >> 1
res = 0
if L <= m:
res = self.query(o * 2, l, m, L, R)
if m < R:
res = max(res, self.query(o * 2 + 1, m + 1, r, L, R))
return res
解题思路:
- 初始化线段树:
- 创建一个
LazySegmentTree实例,其大小为4n(因为线段树通常需要一个额外的空间来存储内部节点)。 - 初始化
cnt数组来存储每个节点的区间内未访问元素的数量。 - 初始化
todo数组来存储延迟的更新操作。 - 调用
build方法来构建线段树,初始时所有元素都是未访问的,所以每个叶子节点的cnt值都设为 1(表示该位置是未访问的)。
- 创建一个
- 处理查询:
- 对于每个查询
(l, r),调用update方法将索引从l+2到r(注意这里的l+2是因为题目可能有特定的索引约定,比如索引 0 和 1 被视为特殊情况,或者为了避免边界问题)之间的所有元素标记为已访问(即将其cnt值更新为 0)。 - 在每次更新后,通过查看根节点的
cnt值(即整个数组的未访问元素数量)减 1 来计算最近的未访问元素与数组起始位置的距离。这是因为每次更新都会使得一个元素从未访问变为已访问,而根节点的cnt值反映了整个数组中未访问元素的数量。减 1 是因为索引是从 0 开始的,而我们需要的是距离,所以需要将计数器的值转换为实际的索引距离(这里假设数组中的元素是连续排列的,没有空缺)。
- 对于每个查询
- 线段树的操作:
build方法用于构建线段树,初始化每个叶子节点的cnt值。maintain方法用于维护节点的cnt值,确保它反映了其子节点的cnt值之和。do方法用于立即更新节点的cnt值和todo值。spread方法用于将延迟的更新操作传播到子节点。update方法用于执行区间更新操作,使用延迟技术来优化性能。query方法虽然在这段代码中未使用,但通常用于查询区间内的某些信息(如最大值、最小值等)。



















