Go实现动态开点线段树
1、线段树介绍
线段树是一种用于高效处理区间查询和区间更新的数据结构,当我们需要解决一个频繁更新区间值的问题的时候,就可以采用线段树的结构进行解决。线段树的核心思想是将区间分为多个子区间进行管理,越往下区间范围越小,根节点表示整个线段树能表示的区间。
本文记录使用Go实现动态开点线段树的方式,该模板的线段树用于解决区间求和问题,还有求解区间最小值、最大值的线段树可以进行微调修改即可。
区间查询、区间更新的时间复杂度均为O(logN)。
2、动态开点线段树实现
动态开点的核心在于,需要缩小范围,即进入子节点的时候再进行创建,相对于使用数组来实现线段树,可以更大的减小空间开销。
1、线段树节点
一个节点需要记录它的左子节点、右子节点、当前节点表示的区间的和val,以及暂未下推给子节点的懒惰值lazy。
type SegTreeNode struct {
lazy int
val int
left *SegTreeNode
right *SegTreeNode
}
2、线段树的创建
整个线段树只需要记录一个根节点以及该线段树表示的区间上届。
type SegTree struct {
//线段树的范围,0~N
N int
root *SegTreeNode
}
// 创建线段树
func CreateSegTree(n int) *SegTree {
return &SegTree{
N: n,
root: &SegTreeNode{
lazy: 0,
val: 0,
left: nil,
right: nil,
},
}
}
3、递归上推
当更新完了子节点后,回到当前节点的时候,需要更新当前节点的值,表示从树的底部上推值。
// 递归上推
func (ST *SegTree) Pushup(node *SegTreeNode) {
node.val = node.left.val + node.right.val
}
4、懒惰下推
当需要缩小查找区间的时候,需要向下查找,这时候要先把懒惰值下推,防止查找出错误的结果,也防止子节点还未创建。
// 同步下推
func (ST *SegTree) Pushdown(node *SegTreeNode, leftnum, rightnum int) {
//创建左右节点
if node.left == nil {
node.left = new(SegTreeNode)
}
if node.right == nil {
node.right = new(SegTreeNode)
}
//下推节点懒惰标记
if node.lazy == 0 {
return
}
node.left.val += leftnum * node.lazy
node.right.val += rightnum * node.lazy
//下推
node.left.lazy += node.lazy
node.right.lazy += node.lazy
//置零
node.lazy = 0
}
首先先创建左右节点,如果没有需要下推的懒惰标记则直接返回。否则就更新左右节点的val和lazy。
5、更新操作
// 更新操作,更新[left,right]区间的值,start和end是当前处在区间
func (ST *SegTree) Update(node *SegTreeNode, start, end, left, right, val int) {
if left <= start && end <= right {
//锁定区间,进行更新
node.val += (end - start + 1) * val
node.lazy += val
return
}
//缩小区间
mid := (start + end) / 2
//需要找到子节点,先下推懒惰标记
ST.Pushdown(node, mid-start+1, end-mid)
if mid >= left {
ST.Update(node.left, start, mid, left, right, val)
}
if mid+1 <= right {
ST.Update(node.right, mid+1, end, left, right, val)
}
//递归
ST.Pushup(node)
}
left和right表示要更新的区间,而start和end表示当前区间。如果当前区间处在需要更新的区间内,则直接更新区间值以及懒惰值,然后直接返回即可,此时不需要继续更新下面节点的值,这是动态开点的关键所在。
若当前区间并未完全处在需要更新的区间内,则二分该区间,缩小范围进行更新。
例如在一次操作需要更新的是[30,40]范围的值,而当前区间处在[25,50]中,当前区间并未完全处在更新区间,则二分为[25,37]和[38,50],左区间和右区间均和需要更新的区间存在交集,那么就往下更新,直到更新区间包含当前区间。
在更新完后,进行一次上推。
6、查询操作
与更新操作类似,只需要一个ans来记录答案并且返回。
// 查询操作,返回区间的值
func (ST *SegTree) Query(node *SegTreeNode, start, end, left, right int) int {
if left <= start && end <= right {
return node.val
}
mid := (start + end) / 2
ST.Pushdown(node, mid-start+1, end-mid)
ans := 0
if left <= mid {
ans += ST.Query(node.left, start, mid, left, right)
}
if mid+1 <= right {
ans += ST.Query(node.right, mid+1, end, left, right)
}
return ans
}
3、尝试题目
[LeetCode我的日程表安排III](732. 我的日程安排表 III - 力扣(LeetCode))
Go实现动态开点线段树的更多相关文章
- [2016湖南长沙培训Day4][前鬼后鬼的守护 chen] (动态开点线段树+中位数 or 动规 or 贪心+堆优化)
题目大意 给定一个长度为n的正整数序列,令修改一个数的代价为修改前后两个数的绝对值之差,求用最小代价将序列转换为不减序列. 其中,n满足小于500000,序列中的正整数小于10^9 题解(引自mzx神 ...
- [bzoj 3531][SDOI2014]旅行(树链剖分+动态开点线段树)
题目:http://www.lydsy.com:808/JudgeOnline/problem.php?id=3531 分析: 对于每个颜色(颜色<=10^5)都建立一颗线段树 什么!那么不是M ...
- 【BZOJ-4636】蒟蒻的数列 动态开点线段树 ||(离散化) + 标记永久化
4636: 蒟蒻的数列 Time Limit: 30 Sec Memory Limit: 256 MBSubmit: 247 Solved: 113[Submit][Status][Discuss ...
- codeforces 893F - Physical Education Lessons 动态开点线段树合并
https://codeforces.com/contest/893/problem/F 题意: 给一个有根树, 多次查询,每次查询对于$x$i点的子树中,距离$x$小于等于$k$的所有点中权值最小的 ...
- codeforces 915E - Physical Education Lessons 动态开点线段树
题意: 最大$10^9$的区间, $3*10^5$次区间修改,每次操作后求整个区间的和 题解: 裸的动态开点线段树,计算清楚数据范围是关键... 经过尝试 $2*10^7$会$MLE$ $10^7$会 ...
- CF915E Physical Education Lessons 动态开点线段树
题目链接 CF915E Physical Education Lessons 题解 动态开点线段树 代码 /* 动态开点线段树 */ #include<cstdio> #include&l ...
- 洛谷P3313 [SDOI2014]旅行(树链剖分 动态开节点线段树)
题意 题目链接 Sol 树链剖分板子 + 动态开节点线段树板子 #include<bits/stdc++.h> #define Pair pair<int, int> #def ...
- NOIP2017 列队——动态开点线段树
Description: Sylvia 是一个热爱学习的女♂孩子. 前段时间,Sylvia 参加了学校的军训.众所周知,军训的时候需要站方阵. Sylvia 所在的方阵中有n×m名学生,方阵的行数为 ...
- 洛谷P3120 [USACO15FEB]牛跳房子(动态开节点线段树)
题意 题目链接 Sol \(f[i][j]\)表示前\(i\)行\(j\)列的贡献,转移的时候枚举从哪里转移而来,复杂度\(O(n^4)\) 然后考虑每一行的贡献,动态开节点线段树维护一下每种颜色的答 ...
- BZOJ4636: 蒟蒻的数列(动态开节点线段树)
题意 题目链接 Sol 直接上动态开节点线段树 因为只有一次询问,所以中途不需要下传标记 #include<bits/stdc++.h> #define LL long long usin ...
随机推荐
- QT日志类SimpleQtLogger的简单记录
在现代软件开发中,日志记录是必不可少的部分.它不仅帮助开发者在调试和维护软件时了解程序的运行状态,还能提供关键的错误信息.对于使用Qt框架开发应用程序的开发者来说,选择一个合适的日志库至关重要.本文将 ...
- vscode实现Markdown实时预览
vscode - 插件搜索: Markdown Preview Enhanced 安装 然后打开vscode预览框,即可. That's ALL
- git log 常用方法
• git log --all 查看所有分支的历史• git log --all --graph 查看图形化的 log 地址• git log --oneline 查看单行的简洁历史.• git lo ...
- Qt/C++编写视频监控系统82-自定义音柱显示
一.前言 通过音柱控件实时展示当前播放的声音产生的振幅的大小,得益于音频播放组件内置了音频振幅的计算,可以动态开启和关闭,开启后会对发送过来的要播放的声音数据,进行运算得到当前这个音频数据的振幅,类似 ...
- Qt编写地图综合应用14-离线地图下载
一.前言 网上其实有很多各种各样的离线地图下载器,大部分都是要收费的,免费的要么是限制了下载的瓦片数量或者级别,要么是下载的瓦片图打上了水印,看起来很难看,由于经常需要用到离线地图,摆脱这个限制,特意 ...
- 一篇文章弄懂 JavaScript 中通过import导入模块的原理
原文链接: 1.import 2.彻底理解JavaScript ES6中的import和export 3.JavaScript ES6中export.import与export default的用法和 ...
- 2024新版本如何配置CLion与cubeMX开发STM32
2024新版本如何配置CLion与cubeMX开发STM32 1. 为什么我要在网上有很多教程的情况下再做一个新版 各种大佬们给出的配置教程原本很详细,但是在时间过了这么久之后已经不完全符合现在的环境 ...
- 【解决问题记录】https网站中请求http资源接口报错与netERRSSLPROTOCOLERROR错误的解决
在线上的CRM网站中,对接OBS做了一个专门的公共服务,公共服务使用的http协议,在页面中调用接口时出现错误:The page at 'xxx' was loaded over HTTPS, but ...
- 深入剖析数据删除操作:DELETE 语句的使用与管理实践
title: 深入剖析数据删除操作:DELETE 语句的使用与管理实践 date: 2025/1/8 updated: 2025/1/8 author: cmdragon excerpt: 数据删除( ...
- 深度学习基础理论————DeepSpeed
DeepSpeed原理 DeepSpeed 是由微软开发的一种深度学习优化库,专为高性能训练和推理而设计,尤其适用于大规模深度学习模型(如 GPT 系列.BERT 等).它通过一系列技术和优化策略,帮 ...