给你两个下标从 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
文章介绍了处理两种数组和一系列操作的算法,包括用枚举法、优化后的算法以及使用线段树来提高区间操作和查询的效率。线段树的使用可以降低时间复杂度,但增加了辅助空间。
1498





