📜  更新前后 [L, R] 数组中元素的总和和最大值(1)

📅  最后修改于: 2023-12-03 15:40:12.800000             🧑  作者: Mango

更新前后 [L, R] 数组中元素的总和和最大值

当我们需要对一个数组中某一段进行更新操作时,需要同时更新该段数组的总和和最大值。

数组元素的总和

我们可以使用前缀和数组来快速计算某一段数组元素的总和。前缀和数组如下所示:

prefix_sum = [0] * (n + 1)
for i in range(1, n + 1):
    prefix_sum[i] = prefix_sum[i - 1] + arr[i - 1]

其中,n 为数组长度,arr 为原始数组。通过上述代码,我们可以快速计算出 prefix_sum[i] 表示前 i 个元素的总和。

当需要更新数组中某一段 [L, R] 时,我们可以通过如下代码计算更新后的总和:

delta = x - arr[i]  # x 表示要更新的值,arr[i] 表示更新前原数组下标为 i 的值
for j in range(L, R + 1):
    prefix_sum[j] += delta

由于该段数组的值已被更新,因此需要将更新后的值 delta 加到该段数组后面所有元素的前缀和上。

数组元素的最大值

在更新完数组的某一段 [L, R] 后,我们还需要重新计算该段数组的最大值。我们可以使用线段树来快速计算某一段数组的最大值。

线段树是一种二叉树数据结构,用于处理区间查询问题。线段树上每个节点都表示一段区间,对于每个节点,其左右子节点对应的区间分别为其父节点区间的左半部分和右半部分。该数据结构有两种基本操作:建树和查询。建树复杂度为 $O(n)$,查询复杂度为 $O(log(n))$。

在本题中,我们需要维护每个区间的最大值。每个节点上还需要维护该节点区间的最大值和次大值。具体实现方法如下:

class SegTree:
    def __init__(self, n):
        self.n = n
        self.tree = [0] * (4 * n)
        self.lazy = [0] * (4 * n)
        self.maxn = [-inf] * (4 * n)
        self.maxn2 = [-inf] * (4 * n)

    def pushup(self, rt):
        self.tree[rt] = self.tree[rt << 1] + self.tree[rt << 1 | 1]
        mx1, idx1 = self.maxn[rt << 1], rt << 1
        mx2, idx2 = self.maxn[rt << 1 | 1], rt << 1 | 1
        if mx1 == mx2:
            self.maxn[rt], self.maxn2[rt] = mx1, max(self.maxn2[idx1], self.maxn2[idx2])
        elif mx1 > mx2:
            self.maxn[rt], self.maxn2[rt] = mx1, max(self.maxn2[idx1], mx2)
        else:
            self.maxn[rt], self.maxn2[rt] = mx2, max(self.maxn2[idx2], mx1)

    def pushdown(self, rt, ln, rn):
        if self.lazy[rt]:
            self.tree[rt << 1] += ln * self.lazy[rt]
            self.tree[rt << 1 | 1] += rn * self.lazy[rt]
            self.lazy[rt << 1] += self.lazy[rt]
            self.lazy[rt << 1 | 1] += self.lazy[rt]
            mx1, mx2 = self.maxn[rt << 1], self.maxn2[rt << 1]
            if self.lazy[rt] < mx1:
                self.maxn[rt << 1] = self.lazy[rt]
            if self.lazy[rt] < mx2:
                self.maxn2[rt << 1] = self.lazy[rt]
            mx1, mx2 = self.maxn[rt << 1 | 1], self.maxn2[rt << 1 | 1]
            if self.lazy[rt] < mx1:
                self.maxn[rt << 1 | 1] = self.lazy[rt]
            if self.lazy[rt] < mx2:
                self.maxn2[rt << 1 | 1] = self.lazy[rt]
            self.lazy[rt] = 0

    def build(self, rt, l, r, arr):
        if l == r:
            self.tree[rt] = arr[l - 1]
            self.maxn[rt] = arr[l - 1]
            return
        mid = (l + r) >> 1
        self.build(rt << 1, l, mid, arr)
        self.build(rt << 1 | 1, mid + 1, r, arr)
        self.pushup(rt)

    def update(self, rt, l, r, L, R, val):
        if L <= l and r <= R:
            self.tree[rt] += (r - l + 1) * val
            mx1, mx2 = self.maxn[rt], self.maxn2[rt]
            if val < mx1:
                self.maxn[rt] = val
            if val < mx2:
                self.maxn2[rt] = val
            self.lazy[rt] += val
            return
        mid = (l + r) >> 1
        self.pushdown(rt, mid - l + 1, r - mid)
        if L <= mid:
            self.update(rt << 1, l, mid, L, R, val)
        if mid < R:
            self.update(rt << 1 | 1, mid + 1, r, L, R, val)
        self.pushup(rt)

    def query(self, rt, l, r, L, R):
        if L <= l and r <= R:
            return self.maxn[rt]
        mid = (l + r) >> 1
        self.pushdown(rt, mid - l + 1, r - mid)
        res = -inf
        if L <= mid:
            res = max(res, self.query(rt << 1, l, mid, L, R))
        if mid < R:
            res = max(res, self.query(rt << 1 | 1, mid + 1, r, L, R))
        return res

在更新完数组中某一段 [L, R] 后,我们可以通过如下代码重新计算该段数组的最大值:

tree.update(1, 1, n, L, R, (x - arr[i]))
max_val = tree.query(1, 1, n, L, R)

其中,tree 表示构建好的线段树,x 表示要更新的值,arr[i] 表示更新前原数组下标为 i 的值。我们将该段数组更新所需要增加的值作为参数传入 tree.update 中。更新完后,再调用 tree.query 函数查询该段数组的最大值。

总结

本文介绍了如何更新一个数组中某一段的元素,并同时重新计算该段数组的总和和最大值。其中,我们使用了前缀和数组来计算总和,使用线段树来计算最大值。当需要更新某一段数组时,我们将需要新增的值加到该段数组之后的所有元素的前缀和上;当重新计算该段数组最大值时,我们调用 tree.update 来更新该段数组并调用 tree.query 来查询该段数组的最大值。