LC 2569. 更新数组后处理求和查询
https://leetcode.cn/problems/handling-sum-queries-after-update/
这题看题解中还有直接模拟超大数的bit count. 按照直觉来说这种办法其实并不容易work, 但是运行时间却比线段树要好很多。
class Solution: def handleQuery(self, nums1: List[int], nums2: List[int], queries: List[List[int]]) -> List[int]: s = sum(nums2) x = int(''.join(map(str, nums1[::-1])), 2) ans = [] for op, l, r in queries: if op == 1: y = 1 << (r - l + 1) - 1 y <<= l x = x ^ y elif op == 2: s += l * x.bit_count() else: ans.append(s) return ans
题解 里面还给出了延迟线段树的解法,我觉得这个是值得学习学习的。
延迟线段树有几个要点:
- 树状数组的要点都是从1开始计算的,分隔点可以是 (l+r)//2
- 递归的时候,想要更新的区间可以不进行分隔,但是作用区间需要分隔。
- 延迟线段树需要设置 `lazy[i]`, 表示它的子树是否已经处理过。如果子树没有处理过的话,那么先处理两个子树。
class Solution: def handleQuery(self, nums1: List[int], nums2: List[int], queries: List[List[int]]) -> List[int]: n = len(nums1) cnt = [0] * (4 * n) lazy = [False] * (4 * n) def maintain(i): cnt[i] = cnt[2 * i] + cnt[2 * i + 1] def build(i, l, r): if l == r: cnt[i] = nums1[l - 1] return m = (l + r) // 2 build(2 * i, l, m) build(2 * i + 1, m + 1, r) maintain(i) return def flip(i, l, r, L, R): def fix(i, l, r): cnt[i] = (r - l + 1) - cnt[i] lazy[i] = not lazy[i] return if l <= L and R <= r: fix(i, L, R) return M = (L + R) // 2 if lazy[i]: # 如果子树没有处理的话,那么需要处理子树先 lazy[i] = False fix(2 * i, L, M) fix(2 * i + 1, M + 1, R) maintain(i) if l <= M: flip(2 * i, l, r, L, M) # 处理区间(l,r)不用拆分 if (M + 1) <= r: flip(2 * i + 1, l, r, M + 1, R) # 但是作用区间(L, R)需要拆分 maintain(i) build(1, 1, n) # 下标从1开始很关键,否则处理起来很麻烦 ans, base = [], sum(nums2) for op, l, r in queries: if op == 1: flip(1, l + 1, r + 1, 1, n) elif op == 2: base += l * cnt[1] else: ans.append(base) return ans