Maximum Segment Sum After Removals

2022/8/21 23:54:27

本文主要是介绍Maximum Segment Sum After Removals,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

Maximum Segment Sum After Removals

You are given two 0-indexed integer arrays $nums$ and $removeQueries$, both of length $n$. For the $i^{th}$ query, the element in $nums$ at the index $removeQueries[i]$ is removed, splitting $nums$ into different segments.

A segment is a contiguous sequence of positive integers in $nums$. A segment sum is the sum of every element in a segment.

Return an integer array $answer$, of length $n$, where $answer[i]$ is the maximum segment sum after applying the $i^{th}$ removal.

Note: The same index will not be removed more than once.

Example 1:

Input: nums = [1,2,5,6,1], removeQueries = [0,3,2,4,1]
Output: [14,7,2,2,0]
Explanation: Using 0 to indicate a removed element, the answer is as follows:
Query 1: Remove the 0th element, nums becomes [0,2,5,6,1] and the maximum segment sum is 14 for segment [2,5,6,1].
Query 2: Remove the 3rd element, nums becomes [0,2,5,0,1] and the maximum segment sum is 7 for segment [2,5].
Query 3: Remove the 2nd element, nums becomes [0,2,0,0,1] and the maximum segment sum is 2 for segment [2]. 
Query 4: Remove the 4th element, nums becomes [0,2,0,0,0] and the maximum segment sum is 2 for segment [2]. 
Query 5: Remove the 1st element, nums becomes [0,0,0,0,0] and the maximum segment sum is 0, since there are no segments.
Finally, we return [14,7,2,2,0].

Example 2:

Input: nums = [3,2,11,1], removeQueries = [3,2,1,0]
Output: [16,5,3,0]
Explanation: Using 0 to indicate a removed element, the answer is as follows:
Query 1: Remove the 3rd element, nums becomes [3,2,11,0] and the maximum segment sum is 16 for segment [3,2,11].
Query 2: Remove the 2nd element, nums becomes [3,2,0,0] and the maximum segment sum is 5 for segment [3,2].
Query 3: Remove the 1st element, nums becomes [3,0,0,0] and the maximum segment sum is 3 for segment [3].
Query 4: Remove the 0th element, nums becomes [0,0,0,0] and the maximum segment sum is 0, since there are no segments.
Finally, we return [16,5,3,0].

Constraints:

$n == nums.length == removeQueries.length$
$1 \leq n \leq {10}^{5}$
$1 \leq nums[i] \leq {10}^{9}$
$0 \leq removeQueries[i] < n$
All the values of $removeQueries$ are unique.

 

解题思路

  这题可以正着做(在线做法),用平衡树(这里用STL的set和multiset实现)。翻着做(离线做法),用并查集。

  比赛的时候是正着做,思路有了并且正确,但死在不熟悉STL和边界情况上了。

  正着做就是用一个集合set来维护若干组区间,每一次操作就相当于将某个区间裂开成两个区间(也有可能是一个区间)。先根据当前要删除的下标$x$找到覆盖这个下标的区间$(left, right)$,这里可以通过set::lower_bound()来实现(这里有个边界要处理,当时找了半天bug都没找到,解析在代码下方)。然后把区间裂成$(left, x-1)$和$(x+1, right)$(当然也可能只有一个区间),同时把区间$(left, right)$删除。同时还要开一个multiset来维护set中各个区间的总和,multiset::rbegin()就是所有区间总和的最大值,这里是把multiset当作堆来使用(当时比赛的时候想到用优先队列来维护,但优先队列不支持删除操作,所以还写了些其他东西来补助这个操作,写得很乱)。

  AC代码如下:

 1 class Solution {
 2 public:
 3     vector<long long> maximumSegmentSum(vector<int>& nums, vector<int>& removeQueries) {
 4         int n = nums.size();
 5         vector<long long> s(n + 1); // 前缀和数组
 6         for (int i = 1; i <= n; i++) {
 7             s[i] = s[i - 1] + nums[i - 1];
 8         }
 9 
10         set<pair<int, int>> st;     // 维护区间
11         st.insert({1, n});
12         multiset<long long> mst;    // 维护各个区间的总和,可以得到最大值
13         mst.insert(s[n]);
14         vector<long long> ans;
15 
16         for (int i = 0; i < n; i++) {
17             int x = removeQueries[i] + 1;
18             auto it = --st.upper_bound({x, n + 1}); // 找到可以覆盖x的区间。{x, n+1}的第二维相当于正无穷,找到左端点严格大于x的区间,那么前一个区间的左端点就一定<=x
19 
20             if (x >= it->first) {   // 裂成左边部分的区间(left, x-1)
21                 st.insert({x + 1, it->second});
22                 mst.insert(s[it->second] - s[x]);
23             }
24             if (x <= it->second) {  // 裂成右边部分的区间(x+1, right)
25                 st.insert({it->first, x - 1});
26                 mst.insert(s[x - 1] - s[it->first - 1]);
27             }
28 
29             mst.erase(mst.find(s[it->second] - s[it->first - 1]));  // 删去mst中区间(left, right)的值,这里要删除迭代器,如果直接删除对应的值,就会把所有相同的值都删除而不是只删这一个
30             st.erase(it);   // 删除区间(left right)
31 
32             ans.push_back(*mst.rbegin());
33         }
34 
35         return ans;
36     }
37 };

  这里记录一个bug。就是在上面代码中的$18$行upper_bound()操作,一开始我传入的值是$\{ {x,x} \}$,然后在跑某组数据时发生执行出错。原因就是,如果此时set中只有一个区间$(1,2)$,而我传入的是$\{ {1,1} \}$,也就是找到严格大于$(1,1)$的pair,可以发现第一维$1=1$,第二维$2>1$,因此有$(1,2) > (1,1)$,因此就会返回区间$(1,2)$的迭代器,而这个迭代器就是st.begin(),如果再执行迭代器减$1$操作,就会越界报错。因此当我们要查找覆盖$x$的区间时,应该把第二维赋值为正无穷(这里下标最大不超过$n$,因此可以用$n+1$来表示正无穷),这样即使pair的第一维相等,也会强制往后比较,这样就避免了这种边界情况。

  反着做就是一开始没有任何数,然后每次都往数轴上加一个数,如果可以合并的话就合并成一个更大的区间,最后整个数组填满了数形成一个与原来一样的$1 \sim n$的区间。可以发现执行过程就是区间不断合并的过程(维护连通性),因此可以用并查集。合并后还需要求新区间的总和,然后用这个总和来更新最大值。

  AC代码如下:

 1 class Solution {
 2 public:
 3     vector<int> fa;
 4     vector<long long> sum;
 5 
 6     int find(int x) {
 7         return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);
 8     }
 9 
10     vector<long long> maximumSegmentSum(vector<int>& nums, vector<int>& removeQueries) {
11         int n = nums.size();
12         for (int i = 0; i < n; i++) {
13             fa.push_back(i);
14             sum.push_back(nums[i]);
15         }
16 
17         vector<bool> vis(n);    // vis[i] == false表示第i个数还没有被加进来
18         vector<long long> ans;
19         long long maxs = 0;
20         for (int i = n - 1; i >= 0; i--) {
21             ans.push_back(maxs);
22             int x = removeQueries[i];
23             vis[x] = true;  // x这个位置的数被加进来
24             if (x - 1 >= 0 && vis[x - 1]) { // 合并x-1位置的连通块
25                 sum[x] += sum[find(x - 1)];
26                 fa[find(x - 1)] = find(x);
27             }
28             if (x + 1 < n && vis[x + 1]) {  // 合并x+1位置的连通块
29                 sum[x] += sum[find(x + 1)];
30                 fa[find(x + 1)] = x;
31             }
32             maxs = max(maxs, sum[x]);
33         }
34         
35         reverse(ans.begin(), ans.end());
36         return ans;
37     }
38 };

 

参考资料

  力扣第85场双周赛:https://www.bilibili.com/video/BV1u14y1t771



这篇关于Maximum Segment Sum After Removals的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程