3165.不包含相邻元素的子序列的最大和
【LetMeFly】3165.不包含相邻元素的子序列的最大和:单点修改的线段树(动态规划)
力扣题目链接:https://leetcode.cn/problems/maximum-sum-of-subsequence-with-non-adjacent-elements/
给你一个整数数组 nums
和一个二维数组 queries
,其中 queries[i] = [posi, xi]
。
对于每个查询 i
,首先将 nums[posi]
设置为 xi
,然后计算查询 i
的答案,该答案为 nums
中 不包含相邻元素 的 子序列 的 最大 和。
返回所有查询的答案之和。
由于最终答案可能非常大,返回其对 109 + 7
取余 的结果。
子序列 是指从另一个数组中删除一些或不删除元素而不改变剩余元素顺序得到的数组。
示例 1:
输入:nums = [3,5,9], queries = [[1,-2],[0,-3]]
输出:21
解释:
执行第 1 个查询后,nums = [3,-2,9]
,不包含相邻元素的子序列的最大和为 3 + 9 = 12
。
执行第 2 个查询后,nums = [-3,-2,9]
,不包含相邻元素的子序列的最大和为 9 。
示例 2:
输入:nums = [0,-1], queries = [[0,-5]]
输出:0
解释:
执行第 1 个查询后,nums = [-5,-1]
,不包含相邻元素的子序列的最大和为 0(选择空子序列)。
提示:
1 <= nums.length <= 5 * 104
-105 <= nums[i] <= 105
1 <= queries.length <= 5 * 104
queries[i] == [posi, xi]
0 <= posi <= nums.length - 1
-105 <= xi <= 105
解题方法:线段树 + DP
对于单次操作,我们可以使用分治的方法来求解。对于一个子区间,我们比较关注的有:区间第一个元素是否被选取、区间最后一个元素是否被选取。也就是说,对于一个子区间,一共有以下4种情况:
- 开头一定不选,结尾一定不选,记为$f00$
- 开头一定不选,结尾可选(也可不选),记为$f01$
- 开头可选(也可不选),结尾一定不选,记为$f10$
- 开头可选(也可不选),结尾可选(也可不选),记为$f11$
那么对于区间$[left, right]$,如何进行分治操作呢?
如果$left==right$,那么这个区间就只有一个元素,这个区间的$f00=f01=f10=0$,$f11=\max(0, nums[left])$。
否则,令$mid = \lfloor\frac{left+right}{2}\rfloor$,递归计算$[left, mid]$和$[mid + 1, right]$两个子区间的4个值并汇总得到这个区间的4个值。
假设左区间为$p$,右区间为$q$,则汇总方式为:
- $f00 = \max(f_p00+f_q10, f_p01+f_q00)$
- $f01 = \max(f_p00+f_q11, f_p01+f_q01)$
- $f10 = \max(f_p10+f_q10, f_p11+f_q00)$
- $f11 = \max(f_p10+f_q11, f_p11+f_q01)$
那么如何应对$len(queries)$次的修改呢?那就需要引入线段树了。
- 对于修改操作,使用线段树实现单点修改,线段树的每个节点维护对应区间的4个值
- 对于查询操作,线段树根节点(整个区间)的$f11$记为所求
时空复杂度分析
- 时间复杂度$O(n+q\log n)$,其中$n=len(nums)$,$q=len(queries)$
- 空间复杂度$O(n)$
AC代码
C++
1 |
|
Python
1 |
|
Java
1 |
|
Go
1 |
|
同步发文于CSDN和我的个人博客,原创不易,转载经作者同意后请附上原文链接哦~
Tisfy:https://letmefly.blog.csdn.net/article/details/143452715