2382-删除操作后的最大子段和

Raphael Liu Lv10

给你两个下标从 0 开始的整数数组 numsremoveQueries ,两者长度都为 n 。对于第 i
个查询,nums 中位于下标 removeQueries[i] 处的元素被删除,将 nums 分割成更小的子段。

一个 子段nums 中连续 整数形成的序列。 子段和 是子段中所有元素的和。

请你返回一个长度为 n 的整数数组 _ _answer ,其中 _ _answer[i]是第 i 次删除操作以后的 最大
子段和。

注意: 一个下标至多只会被删除一次。

示例 1:

**输入:** nums = [1,2,5,6,1], removeQueries = [0,3,2,4,1]
**输出:** [14,7,2,2,0]
**解释:** 用 0 表示被删除的元素,答案如下所示:
查询 1 :删除第 0 个元素,nums 变成 [0,2,5,6,1] ,最大子段和为子段 [2,5,6,1] 的和 14 。
查询 2 :删除第 3 个元素,nums 变成 [0,2,5,0,1] ,最大子段和为子段 [2,5] 的和 7 。
查询 3 :删除第 2 个元素,nums 变成 [0,2,0,0,1] ,最大子段和为子段 [2] 的和 2 。
查询 4 :删除第 4 个元素,nums 变成 [0,2,0,0,0] ,最大子段和为子段 [2] 的和 2 。
查询 5 :删除第 1 个元素,nums 变成 [0,0,0,0,0] ,最大子段和为 0 ,因为没有任何子段存在。
所以,我们返回 [14,7,2,2,0] 。

示例 2:

**输入:** nums = [3,2,11,1], removeQueries = [3,2,1,0]
**输出:** [16,5,3,0]
**解释:** 用 0 表示被删除的元素,答案如下所示:
查询 1 :删除第 3 个元素,nums 变成 [3,2,11,0] ,最大子段和为子段 [3,2,11] 的和 16 。
查询 2 :删除第 2 个元素,nums 变成 [3,2,0,0] ,最大子段和为子段 [3,2] 的和 5 。
查询 3 :删除第 1 个元素,nums 变成 [3,0,0,0] ,最大子段和为子段 [3] 的和 3 。
查询 5 :删除第 0 个元素,nums 变成 [0,0,0,0] ,最大子段和为 0 ,因为没有任何子段存在。
所以,我们返回 [16,5,3,0] 。

提示:

  • n == nums.length == removeQueries.length
  • 1 <= n <= 105
  • 1 <= nums[i] <= 109
  • 0 <= removeQueries[i] < n
  • removeQueries 中所有数字 互不相同

解法:模拟

维护一个 set<int> st 记录当前删掉的下标。当删除下标 pos 时,从 st 中找出第一个比 pos 大的下标 R,以及最后一个比 pos 小的下标 L,我们将把区间 [L + 1, R - 1] 分成 [L + 1, pos - 1] 以及 [pos + 1, R - 1]。再用一个 multiset<int> ms 维护所有子段之和即可。具体写法详见参考代码的注释。复杂度 \mathcal{O}(n \log n)。

参考代码(c++)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Solution {
public:
vector<long long> maximumSegmentSum(vector<int>& nums, vector<int>& removeQueries) {
int n = nums.size();
// 求前缀和,方便我们直接求出区间的和
vector<long long> f(n + 1);
for (int i = 1; i <= n; i++) f[i] = f[i - 1] + nums[i - 1];

set<int> st;
// 放入下标 0 和 n + 1,这样就无需处理边界情况
st.insert(0); st.insert(n + 1);
multiset<long long> ms;
// 一开始只有一个子段和,也就是整个数组之和
ms.insert(f[n]);
vector<long long> ans;
for (int pos : removeQueries) {
pos++;
// 找出第一个比 pos 大的已删除下标 R,以及最后一个比 pos 小的已删除下标 L
auto it = st.upper_bound(pos);
int L = *prev(it), R = *it;
// 删除 pos,把它也放入 st 中
st.insert(pos);
// 删除子段 [L + 1, R - 1] 的和
ms.erase(ms.find(f[R - 1] - f[L]));
// 加入新子段 [L + 1, pos - 1] 的和
if (pos - L - 1 > 0) ms.insert(f[pos - 1] - f[L]);
// 加入新子段 [pos + 1, R - 1] 的和
if (R - pos - 1 > 0) ms.insert(f[R - 1] - f[pos]);
// 求出最大的子段和
if (ms.empty()) ans.push_back(0);
else ans.push_back(*prev(ms.end()));
}
return ans;
}
};
 Comments
On this page
2382-删除操作后的最大子段和