LeetCode 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种情况:

  • 开头一定不选,结尾一定不选,记为 f 00 f00 f00
  • 开头一定不选,结尾可选(也可不选),记为 f 01 f01 f01
  • 开头可选(也可不选),结尾一定不选,记为 f 10 f10 f10
  • 开头可选(也可不选),结尾可选(也可不选),记为 f 11 f11 f11

那么对于区间 [ l e f t , r i g h t ] [left, right] [left,right],如何进行分治操作呢?

  • 如果 l e f t = = r i g h t left==right left==right,那么这个区间就只有一个元素,这个区间的 f 00 = f 01 = f 10 = 0 f00=f01=f10=0 f00=f01=f10=0 f 11 = max ⁡ ( 0 , n u m s [ l e f t ] ) f11=\max(0, nums[left]) f11=max(0,nums[left])

  • 否则,令 m i d = ⌊ l e f t + r i g h t 2 ⌋ mid = \lfloor\frac{left+right}{2}\rfloor mid=2left+right,递归计算 [ l e f t , m i d ] [left, mid] [left,mid] [ m i d + 1 , r i g h t ] [mid + 1, right] [mid+1,right]两个子区间的4个值并汇总得到这个区间的4个值。

    假设左区间为 p p p,右区间为 q q q,则汇总方式为:

    • f 00 = max ⁡ ( f p 00 + f q 10 , f p 01 + f q 00 ) f00 = \max(f_p00+f_q10, f_p01+f_q00) f00=max(fp00+fq10,fp01+fq00)
    • f 01 = max ⁡ ( f p 00 + f q 11 , f p 01 + f q 01 ) f01 = \max(f_p00+f_q11, f_p01+f_q01) f01=max(fp00+fq11,fp01+fq01)
    • f 10 = max ⁡ ( f p 10 + f q 10 , f p 11 + f q 00 ) f10 = \max(f_p10+f_q10, f_p11+f_q00) f10=max(fp10+fq10,fp11+fq00)
    • f 11 = max ⁡ ( f p 10 + f q 11 , f p 11 + f q 01 ) f11 = \max(f_p10+f_q11, f_p11+f_q01) f11=max(fp10+fq11,fp11+fq01)

那么如何应对 l e n ( q u e r i e s ) len(queries) len(queries)次的修改呢?那就需要引入线段树了。

  • 对于修改操作,使用线段树实现单点修改,线段树的每个节点维护对应区间的4个值
  • 对于查询操作,线段树根节点(整个区间)的 f 11 f11 f11记为所求

时空复杂度分析

  • 时间复杂度 O ( n + q log ⁡ n ) O(n+q\log n) O(n+qlogn),其中 n = l e n ( n u m s ) n=len(nums) n=len(nums) q = l e n ( q u e r i e s ) q=len(queries) q=len(queries)
  • 空间复杂度 O ( n ) O(n) O(n)

AC代码

C++
const unsigned int mod = 1e9 + 7;class Solution {
private:vector<array<unsigned int, 4>> tree;  // 00, 01, 10, 11void maintain(int index) {int leftIndex = 2 * index + 1;int rightIndex = 2 * index + 2;tree[index] = {max(tree[leftIndex][1] + tree[rightIndex][0], tree[leftIndex][0] + tree[rightIndex][2]),max(tree[leftIndex][0] + tree[rightIndex][3], tree[leftIndex][1] + tree[rightIndex][1]),max(tree[leftIndex][2] + tree[rightIndex][2], tree[leftIndex][3] + tree[rightIndex][0]),max(tree[leftIndex][2] + tree[rightIndex][3], tree[leftIndex][3] + tree[rightIndex][1])};}void buildTree(vector<int>& nums, int index, int left, int right) {if (left == right) {tree[index] = {0, 0, 0, (unsigned int)max(nums[left], 0)};return;}int mid = (left + right) / 2;buildTree(nums, 2 * index + 1, left, mid);buildTree(nums, 2 * index + 2, mid + 1, right);maintain(index);}void update(int index, int left, int right, int modifiedI, int val) {if (left == right) {tree[index][3] = max(0, val);return;}int mid = (left + right) / 2;if (modifiedI <= mid) {update(2 * index + 1, left, mid, modifiedI, val);} else {update(2 * index + 2, mid + 1, right, modifiedI, val);}maintain(index);}
public:int maximumSumSubsequence(vector<int>& nums, vector<vector<int>>& queries) {tree.resize(nums.size() * 4);buildTree(nums, 0, 0, nums.size() - 1);unsigned int ans = 0;for (vector<int>& query : queries) {update(0, 0, nums.size() - 1, query[0], query[1]);ans = (ans + tree[0][3]) % mod;}return ans;}
};
Python
from typing import ListMOD = 1_000_000_007
class Solution:def maintain(self, index: int) -> None:leftNode = self.tree[2 * index + 1]rightNode = self.tree[2 * index + 2]self.tree[index] = [max(leftNode[0] + rightNode[2], leftNode[1] + rightNode[0]),max(leftNode[0] + rightNode[3], leftNode[1] + rightNode[1]),max(leftNode[2] + rightNode[2], leftNode[3] + rightNode[0]),max(leftNode[2] + rightNode[3], leftNode[3] + rightNode[1])]def build(self, index: int, left: int, right: int) -> None:if left == right:self.tree[index][3] = self.nums[left]returnmid = (left + right) >> 1self.build(2 * index + 1, left, mid)self.build(2 * index + 2, mid + 1, right)self.maintain(index)def update(self, index: int, left: int, right: int, modifiedI: int, val: int) -> None:if left == right:self.tree[index][3] = max(0, val)returnmid = (left + right) >> 1if modifiedI <= mid:self.update(2 * index + 1, left, mid, modifiedI, val)else:self.update(2 * index + 2, mid + 1, right, modifiedI, val)self.maintain(index)def maximumSumSubsequence(self, nums: List[int], queries: List[List[int]]) -> int:self.tree = [[0, 0, 0, 0] for _ in range(len(nums) * 4)]  # 00, 01, 10, 11self.nums = numsself.build(0, 0, len(nums) - 1)ans = 0for q, v in queries:self.update(0, 0, len(nums) - 1, q, v)ans = (ans + self.tree[0][3]) % MODreturn ans
Java
class Solution {private long[][] tree;  // 诶,如果不是long的话会有两组数据无法通过private final int mod = 1000000007;private void maintain(int index) {long[] leftNode = tree[2 * index + 1];long[] rightNode = tree[2 * index + 2];tree[index][0] = Math.max(leftNode[0] + rightNode[2], leftNode[1] + rightNode[0]);tree[index][1] = Math.max(leftNode[0] + rightNode[3], leftNode[1] + rightNode[1]);tree[index][2] = Math.max(leftNode[2] + rightNode[2], leftNode[3] + rightNode[0]);tree[index][3] = Math.max(leftNode[2] + rightNode[3], leftNode[3] + rightNode[1]);}private void build(int[] nums, int index, int left, int right) {if (left == right) {tree[index][3] = Math.max(0, nums[left]);return;}int mid = (left + right) / 2;build(nums, 2 * index + 1, left, mid);build(nums, 2 * index + 2, mid + 1, right);maintain(index);}private void update(int index, int left, int right, int modifiedI, int val) {if (left == right) {tree[index][3] = Math.max(0, val);return;}int mid = (left + right) / 2;if (modifiedI <= mid) {update(2 * index + 1, left, mid, modifiedI, val);} else {update(2 * index + 2, mid + 1, right, modifiedI, val);}maintain(index);}public int maximumSumSubsequence(int[] nums, int[][] queries) {tree = new long[4 * nums.length][4];  // 00, 01, 10, 11build(nums, 0, 0, nums.length - 1);long ans = 0;for (int[] query : queries) {update(0, 0, nums.length - 1, query[0], query[1]);ans = (ans + tree[0][3]) % mod;}return (int)ans;}
}
Go
package maintype data struct {_00 int_01 int_10 int_11 int
}type seg []datafunc max(a int, b int) int {if a >= b {return a}return b
}func maintain(tree seg, index int) {leftNode := tree[index * 2 + 1]rightNode := tree[index * 2 + 2]tree[index] = data{max(leftNode._00 + rightNode._10, leftNode._01 + rightNode._00),max(leftNode._00 + rightNode._11, leftNode._01 + rightNode._01),max(leftNode._10 + rightNode._10, leftNode._11 + rightNode._00),max(leftNode._10 + rightNode._11, leftNode._11 + rightNode._01),}
}func build(tree seg, nums []int, index int, left int, right int) {if left == right {tree[index]._11 = max(0, nums[left])return}mid := (left + right) >> 1build(tree, nums, 2 * index + 1, left, mid)build(tree, nums, 2 * index + 2, mid + 1, right)maintain(tree, index)
}func update(tree seg, index int, left int, right int, modified int, val int) {if left == right {tree[index]._11 = max(0, val)return}mid := (left + right) >> 1if modified <= mid {update(tree, 2 * index + 1, left, mid, modified, val)} else {update(tree, 2 * index + 2, mid + 1, right, modified, val)}maintain(tree, index)
}func maximumSumSubsequence(nums []int, queries [][]int) int {tree := make(seg, len(nums) * 4)build(tree, nums, 0, 0, len(nums) - 1)ans := 0for _, query := range queries {update(tree, 0, 0, len(nums) - 1, query[0], query[1])ans = (ans + tree[0]._11) % 1_000_000_007}return ans
}

同步发文于CSDN和我的个人博客,原创不易,转载经作者同意后请附上原文链接哦~

Tisfy:https://letmefly.blog.csdn.net/article/details/143452715

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/59179.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python代码解析:生成Jieba自定义词典

Python代码解析&#xff1a;生成Jieba自定义词典 引言代码结构概览代码详解1. 导入必要的库2. 定义文件路径3. 读取JSON文件内容4. 生成自定义词典 总结参考资料 引言 在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;分词是一个非常重要的步骤。Jieba是一个非常流…

微信小程序,打开新的项目,调试遇见[ app.json 文件内容错误] app.json: 在项目根目录未找到 app.json

1&#xff0c;首先&#xff0c;在开发工具右上角&#xff0c;打开详情&#xff1b;设置基础库&#xff1b;3.6.3 2&#xff0c;第二步&#xff0c;在项目目录下&#xff0c;找到app.json文件存在 3&#xff0c;第三步&#xff0c;修改项目根目录下&#xff0c;project.config.j…

MFC界面开发组件Xtreme Toolkit Pro v24全新发布—完整的SVG支持

Codejock软件公司的Xtreme Toolkit Pro是屡获殊荣的VC界面库&#xff0c;是MFC开发中最全面界面控件套包&#xff0c;它提供了Windows开发所需要的11种主流的Visual C MFC控件&#xff0c;包括Command Bars、Controls、Chart Pro、Calendar、Docking Pane、Property Grid、Repo…

FreeRTOS学习8——开启任务调度器API函数简介

开启任务调度器API函数简介 任务调度开启任务调度器API函数简介**函数** **vTaskStartScheduler()****函数** **xPortStartScheduler()****函数** **prvStartFirstTask()****函数** **vPortSVCHandler()****注意**补充**出栈/压栈汇编指令详解** 任务调度 开启任务调度器API函…

SIGNAL TAP使用记录

一、首先编译工程 二、打开signal tap&#xff0c;并设置抓取时钟以及采样深度 二、点击set up&#xff0c;然后双击空白处&#xff0c;会弹出右侧窗口&#xff0c;点击filter选择pre_synthesis&#xff0c;这里选择综合前的信号观测&#xff0c;要确保左侧窗口内的信号是黑色…

RAID(Redundant Array of Independent Disks,独立冗余磁盘阵列)

在计算机组成原理中&#xff0c;RAID&#xff08;Redundant Array of Independent Disks&#xff0c;独立冗余磁盘阵列&#xff09;是一种将多个物理磁盘驱动器组合成一个或多个逻辑单元&#xff0c;以提供数据存储的技术。RAID技术旨在通过数据冗余和分散存储来提高数据的可靠…

Windows版 nginx安装,启动,目录解析,常用命令

Windows版 nginx安装&#xff0c;启动&#xff0c;目录解析&#xff0c;常用命令 一级目录二级目录三级目录 1. 下载2. 启动方式一&#xff1a;方式二&#xff1a; 3. 验证是否启动4. 安装目录解析5. 常用命令 一级目录 二级目录 三级目录 1. 下载 官网下载&#xff1a;ngi…

【Linux 从基础到进阶】使用Pacemaker与Corosync实现高可用

使用Pacemaker与Corosync实现高可用 在现代 IT 基础设施中&#xff0c;高可用性&#xff08;High Availability&#xff0c;HA&#xff09;至关重要&#xff0c;尤其是在处理关键应用和服务时。本文将介绍如何使用 Pacemaker 和 Corosync 实现高可用性集群&#xff0c;以确保服…

kafka相关面试题

文章目录 什么是消息中间件&#xff1f;kafka 是什么&#xff1f;有什么作用&#xff1f;kafka 的架构是怎么样的&#xff1f;Kafka Replicas是怎么管理的&#xff1f;如何确定当前能读到哪一条消息&#xff1f;生产者发送消息有哪些模式&#xff1f;发送消息的分区策略有哪些&…

Python | Leetcode Python题解之第519题随机翻转矩阵

题目&#xff1a; 题解&#xff1a; class Solution:def __init__(self, m: int, n: int):self.m mself.n nself.total m * nself.map {}def flip(self) -> List[int]:x random.randint(0, self.total - 1)self.total - 1# 查找位置 x 对应的映射idx self.map.get(x,…

SHEEL脚本编程

一、shell基本知识 Ⅰ、为什么要学习和使用shell编程 通过编程&#xff0c;简化日常的维护工作&#xff0c;使得管理员从简单的重复劳动解脱出来 Ⅱ、什么是shell shell的功能 Shell又称命令解释器&#xff0c;它能识别用户输入的各种命令&#xff0c;并传递给操作系统。它…

三、Kafka集群

一、Kafka集群的概念 1、目的 高并发、高可用、动态扩展。 主备数据架构、双活节点、灾备数据中心。 如果是服务的地理范围过大也可以使不同的集群节点服务不同的区域&#xff0c;降低网络延迟。 2、Kafka集群的基本概念 1&#xff09;复制&#xff08;镜像&#xff09; kaf…

关于Android Studio Koala Feature Drop | 2024.1.2下载不了插件的解决办法

解决 androidStudio Settings->Plugins下载插件&#xff0c;点击install后没反应&#xff0c;同时插件描述相关显示不出来 第一步&#xff1a; 第二步&#xff1a; 点击设置&#xff0c;勾选Auto-detect proxy settings&#xff0c;输入网址 https://plugins.jetbrains.com…

近期学习前端的心得

1.如果你这一行的编辑权利在于你这一行的某个字段的值&#xff0c;你可以使用这样:disabled"scope.row.某字段 ! 某字段的值" 2.如果你不想使用弹出框的形式来修改数据库&#xff0c;可以采用 对“某字段”列使用了 el-input&#xff0c;并绑定了 v-model 到 sco…

笔记本双系统win10+Ubuntu 20.04 无法调节亮度亲测解决

sudo add-apt-repository ppa:apandada1/brightness-controller sudo apt-get update sudo apt-get install brightness-controller-simple 安装好后找到一个太阳的图标&#xff0c;就是这个软件&#xff0c;打开后调整brightness&#xff0c;就可以调整亮度&#xff0c;可…

若依微服务架构遇到的一些问题记录

一、nacos启动问题 需要看官网的准备工作&#xff0c;认真看&#xff0c;版本问题卡了两天 https://doc.ruoyi.vip/ruoyi-cloud/document/hjbs.html#%E5%87%86%E5%A4%87%E5%B7%A5%E4%BD%9C 1.下载nacos&#xff0c;版本需要对应上 版本说明链接 2.记得运行数据库&#xff0…

语音合成技术:AI如何模仿人类声音

大家好&#xff0c;我是Shelly&#xff0c;一个专注于输出AI工具和科技前沿内容的AI应用教练&#xff0c;体验过300款以上的AI应用工具。关注科技及大模型领域对社会的影响10年。关注我一起驾驭AI工具&#xff0c;拥抱AI时代的到来。 AI工具集1&#xff1a;大厂AI工具【共23款…

【客户端开发】electron 中无法使用 js-cookie 的问题

产生问题的原因 谷歌浏览器升级之后&#xff0c;出于安全考虑&#xff0c;cookie的SameSite属性默认值由None变为Lax&#xff0c;对于跨域的请求&#xff0c;禁止携带cookie。electron内核是chromium内核,所以也会有这个限制。 Cookie的SameSite属性用来限制第三方 Cookie&…

Linux_shell编程

shell介绍 概念: 用户编写的shell命令通过shell解释器解释后交给linux内核去执行. shell是一个程序(解释器程序) 用户和linux内核的桥梁. Shell 是一个 C 语言编写的脚本语言&#xff0c;它是用户与 Linux 的桥梁&#xff0c;用户输入命令交给 Shell 处理 Shell 将相应的操作传…

Java 多线程(八)—— 锁策略,synchronized 的优化,JVM 与编译器的锁优化,ReentrantLock,CAS

前言 本文为 Java 面试小八股&#xff0c;一句话&#xff0c;理解性记忆&#xff0c;不能理解就死背吧。 锁策略 悲观锁与乐观锁 悲观锁和乐观锁是锁的特性&#xff0c;并不是特指某个具体的锁。 我们知道在多线程中&#xff0c;锁是会被竞争的&#xff0c;悲观锁就是指锁…