Leetcode 2569. 更新数组后处理求和查询

1.题目基本信息

1.1.题目描述

给你两个下标从 0 开始的数组 nums1 和 nums2 ,和一个二维数组 queries 表示一些操作。总共有 3 种类型的操作:

  1. 操作类型 1 为 queries[i] = [1, l, r] 。你需要将 nums1 从下标 l 到下标 r 的所有 0 反转成 1 并且所有 1 反转成 0 。l 和 r 下标都从 0 开始。

  2. 操作类型 2 为 queries[i] = [2, p, 0] 。对于 0 <= i < n 中的所有下标,令 nums2[i] = nums2[i] + nums1[i] * p 。

  3. 操作类型 3 为 queries[i] = [3, 0, 0] 。求 nums2 中所有元素的和。

请你返回一个 数组,包含 所有第三种操作类型 的答案。

1.2.题目地址

https://leetcode.cn/problems/handling-sum-queries-after-update/description/

2.解题方法

2.1.解题思路

带懒标记的线段树

2.2.解题步骤

第一步,基于nums1构建线段树(线段树的实现请看代码及注释)

第二步,计算sums2的和total;遍历queries,在线段树上执行相关操作,并不断更新total

第三步,更新后的total即为题解,返回即可

3.解题代码

python代码

class SegNode():
    def __init__(self, id:int):
        self.id = id
        self.start = 0
        self.end = 0
        self.sum = 0
        self.lazy = 0
    
    def __str__(self):
        return f"id = {self.id}, start = {self.start}, end = {self.end}, sum = {self.sum}, lazy = {self.lazy}"

# ==> 带懒标记 求和(变体) 的线段树(节点类实现)
class SegTree:
    def __init__(self, nums:list[int]):
        # 初始化线段树的存储数组并进行构建二叉平衡线段树(这里采用平衡二叉树,也可以用最大堆进行构建)
        self.n = len(nums)
        self.nums = nums    # 维护原数组的值
        self.tree = [SegNode(i) for i in range(self.n * 4)]   # 二叉平衡树的范围为4*n,如果使用最大堆的自底向上,则范围2*n即可
        self.build(nums, 0, 0, self.n - 1)
    
    # 基础方法:构建树,在node树中,将nums[start:end+1]中的区间元素进行插入(nodeId、start、end确定一个线段树结点)
    def build(self, nums:list[int], nodeId:int, start:int, end:int) -> None:
        node = self.tree[nodeId]
        if start == end:
            node.sum = nums[start]
            node.start = start
            node.end = end
            return 
        mid = (end - start) // 2 + start
        # 构建左子树
        self.build(nums, nodeId * 2 + 1, start, mid)
        # 构建右子树
        self.build(nums, nodeId * 2 + 2, mid + 1, end)
        # 和形式的线段树;tree[nodeId].sum记录nums[start:end+1]之间元素的和
        node.sum = self.tree[nodeId * 2 + 1].sum + self.tree[nodeId * 2 + 2].sum
        node.start = start
        node.end = end
    
    # 结点懒标记向下推送(基础函数):将node结点的懒标记推送到左右子结点中,并将自身的懒标记清空
    def pushDown(self, node:SegNode) -> None:
        # 第一步,特殊判断。node结点的懒标记值为0,无需向下推送,直接退出
        if node.lazy == 0:
            return 
        # 第二步,获取左右结点的结点编号和范围,更新左右结点的sum和lazy属性
        start, end = node.start, node.end
        mid = (end - start) // 2 + start
        leftChild = self.tree[node.id * 2 + 1]
        rightChild = self.tree[node.id * 2 + 2]
        if node.lazy % 2 == 1:
            leftChild.sum = mid - start + 1 - leftChild.sum
            leftChild.lazy += node.lazy
            rightChild.sum = end - mid - rightChild.sum
            rightChild.lazy += node.lazy
        # 第三步,清空node对应的lazy值
        node.lazy = 0
    
    def rangeReverse(self, left:int, right:int, nodeId:int) -> None:
        node = self.tree[nodeId]
        # 第一步,递归退出条件
        # 1.1.node结点区间[start,end]和目标区间[left,right]不存在交集时,直接退出
        if left > node.end or right < node.start:
            return 
        # 1.2.node结点区间[start,end]被目标区间[left,right]包含,直接更新node的lazy和sum数组。tree中node.sum结点对应值自增;在lazy数组中更新node结点的懒标记,node.lazy自增value
        if left <= node.start and right >= node.end:
            node.sum = node.end - node.start + 1 - node.sum
            node.lazy += 1
            return
        # 第二步,node区间和目标区间存在重叠的情况下,递归处理
        # 2.1.由于需要分到两个子区间中进行递归操作,所以node对应的lazy值需要向下推送到子结点中;本质就是将node结点的lazy值分配到左右结点中,更新node的lazy和sum属性
        self.pushDown(node)
        # 2.2.递归范围更新两个子树
        self.rangeReverse(left, right, nodeId * 2 + 1)
        self.rangeReverse(left, right, nodeId * 2 + 2)
        # 第三步,更新当前结点的sum值(node中增加value的范围不确定,所以通过子结点来更新)
        node.sum = self.tree[nodeId * 2 + 1].sum + self.tree[nodeId * 2 + 2].sum

    # 区间查询(基础方法):在结点node对应的[start,end]区间中,求[left,right]区间部分的原数组值的和
    def rangeSum(self, left:int, right:int, nodeId:int) -> int:
        node = self.tree[nodeId]
        # 第一步,递归退出条件
        # 1.1.node结点区间和目标区间[left,right]不存在交集时,直接退出
        if left > node.end or right < node.start:
            return 0
        # 1.2.node结点区间被目标区间[left,right]包含,直接返回tree数组中node结点的sum属性
        if left <= node.start and right >= node.end:
            return node.sum
        # 第二步,node区间和目标区间存在重叠的情况下,递归处理
        # 2.1.由于需要分到两个子区间中进行递归操作,所以node对应的lazy值需要向下推送到子结点中;本质就是将node结点的lazy值分配到左右结点中,更新node的lazy和sum属性
        self.pushDown(node)
        # 2.2.递归获取两个子树的范围和,相加进行返回
        mid = (node.end - node.start) // 2 + node.start
        return self.rangeSum(left, right, nodeId * 2 + 1) + self.rangeSum(left, right, nodeId * 2 + 2)

    def __str__(self):
        return f"{[t.sum for t in self.tree]}"


class Solution:
    def handleQuery(self, nums1: List[int], nums2: List[int], queries: List[List[int]]) -> List[int]:
        # 思路:带懒标记的线段树
        n = len(nums1)
        # 第一步,基于nums1构建线段树
        segTree = SegTree(nums1)
        # 第二步,计算sums2的和total;遍历queries,在线段树上执行相关操作,并不断更新total
        total = sum(nums2)
        result = []
        for a, b, c in queries:
            if a == 1:
                segTree.rangeReverse(b, c, 0)
            elif a == 2:
                total += b * segTree.rangeSum(0, n - 1, 0)
            else:
                result.append(total)
        # 第三步,更新后的total即为题解,返回即可
        return result

4.执行结果

posted @ 2025-06-23 10:39  Geek0070  阅读(23)  评论(0)    收藏  举报