【leetcode天天练】2569. 更新数组后处理求和查询

给你两个下标从 0 开始的数组 nums1 和 nums2 ,和一个二维数组 queries 表示一些操作。总共有 3 种类型的操作:
操作类型 1 为 queries[i] = [1, l, r] 。你需要将 nums1 从下标 l 到下标 r 的所有 0 反转成 1 或将 1 反转成 0 。l 和 r 下标都从 0 开始。
操作类型 2 为 queries[i] = [2, p, 0] 。对于 0 <= i < n 中的所有下标,令 nums2[i] = nums2[i] + nums1[i] * p 。
操作类型 3 为 queries[i] = [3, 0, 0] 。求 nums2 中所有元素的和。
请你返回一个数组,包含所有第三种操作类型的答案。

代码:

class Solution:
    def handleQuery(self, nums1: List[int], nums2: List[int], queries: List[List[int]]) -> List[int]:

示例:

输入:nums1 = [1,0,1], nums2 = [0,0,0], queries = [[1,1,1],[2,1,0],[3,0,0]]
输出:[3]
解释:第一个操作后 nums1 变为 [1,1,1] 。第二个操作后,nums2 变成 [1,1,1] ,所以第三个操作的答案为 3 。所以返回 [3] 。

图示:
方法1:
枚举法,按步骤模拟操作

O(n^2)

class Solution:
    def handleQuery(self, nums1: List[int], nums2: List[int], queries: List[List[int]]) -> List[int]:
        # 操作1
        def q1(nums1,l,r):
            ans = nums1
            while l<=r:
                if ans[l]==0:
                    ans[l]=1
                elif ans[l]==1:
                    ans[l]=0
                l+=1
            return ans

        def q2(nums2,nums1,p):
            ans = nums2
            for i in range(len(nums1)):
                ans[i] = nums2[i]+nums1[i]*p
            return ans
        
        def q3(nums2):
            ans = sum(nums2)
            return ans
        
        ans = []
        
        for q in queries:
            if q[0]==1 :
                nums1 = (q1(nums1,q[1],q[2]))
                #print(nums1)
            elif q[0]==2:
                nums2 = (q2(nums2,nums1,q[1]))
                #print(nums2)
            elif q[0]==3:
                ans.append(q3(nums2))
        
        return ans

方法二
实际上时对于操作2,操作3实现做了改进,仍然超时,由于操作一的枚举翻转太耗时

class Solution:
    def handleQuery(self, nums1: List[int], nums2: List[int], queries: List[List[int]]) -> List[int]:
        
        ans = []
        n = len(nums1)
        sum1 = sum(nums1)
        sum2 = sum(nums2)
        for q in queries:
            if q[0]==1 :
                l,r = q[1],q[2]
                while l<=r:
                    if nums1[l]==1:
                        nums1[l]=0
                    else:
                        nums1[l]=1
                    l+=1
                sum1 = sum(nums1)
            elif q[0]==2:
                sum2 = sum1*q[1] + sum2
                #print(nums2)
            elif q[0]==3:
                ans.append(sum2)
        
        return ans

方法三
优化操作一,由于数组1的元素非1即0。
故可推出以下特性及思路:
由方法二可知,对于操作1,只有两个要求:
1.数组应该随操作更改
2.记录数组1的和

  • sum(nums1[l:r+1]),就可以表示出一个区间内1的数量
  • 求和:反转某个区间[l:r]的0,1后数组1的和 = sum(nums1[:l])+ [(r-l+1)-(sum(nums[l:r+1]))]+sum(nums1[r+1:])
  • 更改:将数组1转换为一个字符串,将区间内的01字符串转换为二进制数逐位与‘1’异或运算
class Solution:
    def handleQuery(self, nums1: List[int], nums2: List[int], queries: List[List[int]]) -> List[int]:
        ans, len1, sum2, s1 = [], sum(nums1), sum(nums2), ''.join(str(x) for x in nums1)
        for q in queries:
            if   q[0] == 1:                
                len1 += (l_ := q[2] - q[1] + 1) - (s1[q[1]: q[2] + 1].count('1') << 1)
                s1 = s1[:q[1]] + bin(int(s1[q[1]: q[2] + 1], 2) ^ int('1' * l_, 2))[2:].zfill(l_) + s1[q[2] + 1 :]
            elif q[0] == 2:
                sum2 += q[1] * len1
            elif q[0] == 3:
                ans.append(sum2)
        return ans

成功运行,但耗时仍然较高

方法四,考虑线段树这一数据结构
提高区间求和,以及修改元素的效率O(NlogM)
缺点:建树会增加辅助空间。O(2*n)

class Solution:
    def handleQuery(self, nums1: List[int], nums2: List[int], queries: List[List[int]]) -> List[int]:
        n = len(nums1)
        m = len(queries)
        seg_tree = SegTree(nums1)

        total = sum(nums2)
        ans = []
        for i in range(m):
            if queries[i][0] == 1:
                l = queries[i][1]
                r = queries[i][2]
                seg_tree.reverse_range(l, r)
            elif queries[i][0] == 2:
                total += seg_tree.sum_range(0, n - 1) * queries[i][1]
            elif queries[i][0] == 3:
                ans.append(total)
        return ans


class SegTree:
    def __init__(self, nums):
        n = len(nums)
        self.arr = [SegNode() for _ in range(n * 4 + 1)]
        self.build(1, 0, n - 1, nums)

    def sum_range(self, left, right):
        return self.query(1, left, right)

    def reverse_range(self, left, right):
        self.modify(1, left, right)

    def build(self, id, l, r, nums):
        arr = self.arr
        arr[id] = SegNode()
        arr[id].l = l
        arr[id].r = r
        arr[id].lazytag = False
        if l == r:
            arr[id].sum = nums[l]
            return
        mid = (l + r) >> 1
        self.build(2 * id, l, mid, nums)
        self.build(2 * id + 1, mid + 1, r, nums)
        arr[id].sum = arr[2 * id].sum + arr[2 * id + 1].sum

    # pushdown函数:下传懒标记,即将当前区间的修改情况下传到其左右孩子结点
    def pushdown(self, x):
        arr = self.arr
        if arr[x].lazytag:
            arr[2 * x].lazytag = not arr[2 * x].lazytag
            arr[2 * x].sum = arr[2 * x].r - arr[2 * x].l + 1 - arr[2 * x].sum
            arr[2 * x + 1].lazytag = not arr[2 * x + 1].lazytag
            arr[2 * x + 1].sum = arr[2 * x + 1].r - arr[2 * x + 1].l + 1 - arr[2 * x + 1].sum
            arr[x].lazytag = False
    # 区间修改
    def modify(self, id, l, r):
        arr = self.arr
        if arr[id].l >= l and arr[id].r <= r:
            arr[id].sum = (arr[id].r - arr[id].l + 1) - arr[id].sum
            arr[id].lazytag = not arr[id].lazytag
            return
        self.pushdown(id)
        mid = (arr[id].l + arr[id].r) >> 1
        if arr[2 * id].r >= l:
            self.modify(2 * id, l, r)
        if arr[2 * id + 1].l <= r:
            self.modify(2 * id + 1, l, r)
        arr[id].sum = arr[2 * id].sum + arr[2 * id + 1].sum

    # 区间查询
    def query(self, id, l, r):
        arr = self.arr
        if arr[id].l >= l and arr[id].r <= r:
            return arr[id].sum
        if arr[id].r < l or arr[id].l > r:
            return 0
        self.pushdown(id)
        mid = (arr[id].l + arr[id].r) >> 1
        res = 0
        if arr[2 * id].r >= l:
            res += self.query(2 * id, l, r)
        if arr[2 * id + 1].l <= r:
            res += self.query(2 * id + 1, l, r)
        return res

class SegNode:
    def __init__(self):
        self.l = 0
        self.r = 0
        self.sum = 0
        self.lazytag = False
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值