当前位置: 首页 > news >正文

关于力扣3721. 最长平衡子数组 II线段树解法的反思

学习的灵神的代码

这题有几点观察:

1.由于计算奇偶的平衡,那么用两数之和思想,寻找前面第一个前缀和等值的位置,维护区间长度最大值即可。

2.从左到右扫的时候,对于确定该区间是否存在某数,只需要左边的边界大于等于该数 在扫描区间的最后一次出现的位置。

根据这个特性,我们维护在这个扫描区间中这个数最后一次出现的位置。

(1)假如这个数第一次出现,也就是维护的最后一次出现的位置在更新前不存在时,那么对于idx

idx_{firstOcurance} <= idx < idx_{secondOcurance}

的区段,都应该计算这个数的贡献。这时候因为当到达idx_{secondOccurance}之前,这个贡献都合法,而我们在扫描到当前位置的当下还不知道下一个出现位置在哪里或者会不会出现下一个位置,所以我们大胆地将[idx_{firstOcurrence},n-1]的区间都加上这个贡献,并在到达idx_{secondOccurance}时对贡献进行校正即可。

(2)假如这个数不是第一次出现,因为我们在他第上一次出现的时候,已经把他的位置到这个数组最后的位置加上了该数的贡献,那么我们需要根据我们维护的上一个最后一次出现的位置减去贡献,具体操作是减去[idx_{preOcurrence},idx_{now}-1]区间的贡献,然后加上[idx_{now-1},n-1]区间的贡献。

根据这两个操作,我们就能正确地计算前缀区间的贡献和。

(3)贪心思想和离散介值定理:我们要得到最长的奇偶等量的数组长度,当前位置已经确定了,我们只需要知道等值的最前面的点。所以我们用线段树二分的思想进行寻找。

然后线段树为了优化时间复杂度,需要在顶部就能知道在某一区间是否具有目标信息。这时候灵神使用的是离散介值定理,因为可以通过观察得到,相邻前缀区间的差值小于等于一,那么在一个区间中的最大值和最小值包含了目标值,那么就可以断定这个目标值必然在其中,反之则可以知道没有。这个观察相当巧妙。然后通过这个观察我能学到的是,0 1的连续变化,可以根据离散介值定理实现线段树的搜索。

min = lambda a,b: b if b  < a else a 
max = lambda a,b:b if b > a else a 
class Node:__slots__ = 'min','max','todo'def __init__(self):self.min = self.max = self.todo = 0 
class LazySegmentTree:def __init__(self,n):self._n = n self._tree  = [Node() for _  in range(2<<(n -1).bit_length())]# def _apply(self,node,todo):#     cur = self._tree[node]#     cur.min += todo #     cur.max += todo #     cur.todo += todo # 把当前节点的懒标记下传给左右儿子# def _spread(self, node: int) -> None:#     todo = self._tree[node].todo#     if todo == 0:  # 没有需要下传的信息#         return#     self._apply(node * 2, todo)#     self._apply(node * 2 + 1, todo)#     self._tree[node].todo = 0  # 下传完毕# 把懒标记作用到 node 子树def _apply(self, node: int, todo: int) -> None:cur = self._tree[node]cur.min += todocur.max += todocur.todo += todo# 把当前节点的懒标记下传给左右儿子def _spread(self, node: int) -> None:todo = self._tree[node].todoif todo == 0:  # 没有需要下传的信息returnself._apply(node * 2, todo)self._apply(node * 2 + 1, todo)self._tree[node].todo = 0  # 下传完毕def _maintain(self,node):l_node = self._tree[node*2]r_node = self._tree[node*2 +1]self._tree[node].min = min(l_node.min ,r_node.min)self._tree[node].max = max(l_node.max,r_node.max)def _update(self, node: int, l: int, r: int, ql: int, qr: int, f: int) -> None:if ql <= l and r <= qr:  # 当前子树完全在 [ql, qr] 内self._apply(node, f)returnself._spread(node)m = (l + r) // 2if ql <= m:  # 更新左子树self._update(node * 2, l, m, ql, qr, f)if qr > m:  # 更新右子树self._update(node * 2 + 1, m + 1, r, ql, qr, f)self._maintain(node)def _find_first(self, node: int, l: int, r: int, ql: int, qr: int, target: int) -> int:if l > qr or r < ql or not self._tree[node].min <= target <= self._tree[node].max:return -1if l == r:return lself._spread(node)m = (l + r) // 2idx = self._find_first(node * 2, l, m, ql, qr, target)if idx < 0:# 去右子树找idx = self._find_first(node * 2 + 1, m + 1, r, ql, qr, target)return idxdef update(self, ql: int, qr: int, f: int) -> None:self._update(1, 0, self._n - 1, ql, qr, f)# 查询 [ql, qr] 内第一个等于 target 的元素下标# 找不到返回 -1# 0 <= ql <= qr <= n-1# 时间复杂度 O(log n)def find_first(self, ql: int, qr: int, target: int) -> int:return self._find_first(1, 0, self._n - 1, ql, qr, target)class Solution:def longestBalanced(self, nums: List[int]) -> int:n = len(nums)t = LazySegmentTree(n + 1)last = {}  # nums 的元素上一次出现的位置ans = cur_sum = 0for i, x in enumerate(nums, 1):v = 1 if x % 2 else -1j = last.get(x, 0)if j == 0:  # 首次遇到 xcur_sum += vt.update(i, n, v)  # sum[i:] 增加 velse:  # 再次遇到 xt.update(j, i - 1, -v)  # 撤销之前对 sum[j:i] 的增加last[x] = i# 把 i-1 优化成 i-1-ans,因为在下标 > i-1-ans 中搜索是没有意义的,不会把答案变大j = t.find_first(0, i - 1 - ans, cur_sum)if j >= 0:ans = i - j  # 如果找到了,那么答案肯定会变大return ans

http://www.dtcms.com/a/560640.html

相关文章:

  • Visual Basic.NET 的特性
  • LabVIEW的Vision边缘工具(Edge Tool)功能
  • LabVIEW工业零件尺寸测量
  • 网站建设自建的优点百度指数在线查询前100
  • 【AI智能体】Docker 部署 Coze应用服务实战操作详解
  • QAxios研发笔记(二):在Qt环境下基于Promise风格简化Http的Post请求
  • 用 Flink CDC 将 MySQL 实时同步到 StarRocks
  • 基础开发工具---软件包装管理器及vim
  • 邮箱登陆嵌入网站义乌网站建设方案详细
  • 榨干 CPU 性能:通过绑核将 Redis 尾延迟减半!
  • 数据结构之栈和队列-队列
  • 十九、STM32的TIM(十)(编码器)
  • FSDP(Fully Sharded Data Parallel)全分片数据并行详解
  • Transformer 模型详解:从输入到输出的全流程剖析
  • 网站开发工单营销型网站建设设定包括哪些方面
  • EF Core 数据库迁移
  • 【攻防实战】通达OA文件上传联动Cobalt Strike打穿三层内网(下)
  • 网站备案 教程广州花都区网站建设
  • FPC-40P-05转接板-嘉立创EDA设计
  • Java核心概念深度解析:从包装类到泛型的全面指南
  • 灵途科技亮相NEPCON ASIA 2025 以光电感知点亮具身智能未来
  • flash-attn安装过程中编译错误
  • 世界最受欢迎的免费架站平台经营网站 备案信息
  • 7.1 阴影贴图
  • Elastic AI agent builder 介绍(三)
  • React18中在有路由的情况下父组件如何给子组件传递数据?
  • 边缘计算和云计算有什么区别?
  • 做哪种网站流量大嵌入式软件开发工程师工作内容
  • 【第二十周】机器学习笔记09
  • Linux定时任务:crontab使用教程(附案例)