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 ...
随机推荐
- Vue组件样式穿透
前情 Vue是目前主流的前端框架之一,我是Vue框架的忠实老用户,平时开发中如果再依赖个第三方组件库那样开发效率就会事半功倍. 第三方组件库可以很大提高开发效率,但是叫会有一些UI效果需要自定义,如想 ...
- CMake构建学习笔记19-OpenSSL库的构建
1. 概述 OpenSSL是一个开源的加密工具包和库,主要实现了安全套接字层(SSL)和传输层安全(TLS)协议,以及各种加密算法.数字签名.消息摘要.加密证书等功能.这个库可以说是Web开发尤其是H ...
- Docker Install on Ubuntu
https://docs.docker.com/engine/install/ubuntu/ https://docs.docker.com/compose/install/linux/
- Qt编写地图综合应用25-echart动态交互
一.前言 之前用echart组件做过各种效果,随着各种现场应用现场项目的增多,各种需求也都慢慢增加起来,为了满足各种不同类型的需求,近期又抽空重新整理和封装了echart类,主要就是增加了不少的js函 ...
- 多语言越狱很棒!😊MULTILINGUAL JAILBREAK CHALLENGES IN🧜♀️LARGE LANGUAGE MODELS
写这篇的时候第一次认识到了附录内容的重要性(bushi只是这篇读了全部的附录),它可以让你明确文章全部的实验,并且其中包含很多辅助你对正文理解的表格 禁止盗用,侵权必究!!!欢迎大家积极举报
- .NET 9 new features-Memory Caching 和 Collections 优化
一.内存缓存(Memory Caching) 1. 具体的原理 内存缓存是一种在应用程序内存中存储数据的机制,旨在减少对外部数据源的频繁访问,从而提高应用程序的性能. 在 .NET 中,MemoryC ...
- 2024新版本如何配置CLion与cubeMX开发STM32
2024新版本如何配置CLion与cubeMX开发STM32 1. 为什么我要在网上有很多教程的情况下再做一个新版 各种大佬们给出的配置教程原本很详细,但是在时间过了这么久之后已经不完全符合现在的环境 ...
- CF div2 996(A~D)
手速场,前三题偏简单,后三题偏难.赛时三题,C题做法麻烦了些导致过得不是很快,但最后打得也不是很差. A 两只青蛙相邻时,可移动的青蛙一定输:直接把该青蛙挤到边缘即可. 而是否相邻可以直接用二者初始间 ...
- MSXML2.DOMDocument加载Internet资源
MSXML2.DOMDocument加载Internet资源 The information in this article applies to: Microsoft XML, versions 2 ...
- 编写高性能java程序
为什么你写的程序运行效率不高:为什么你的程序会OutOfMemory:为什么你的程序会经常OutOfMemory:看看你的编程习惯是否符合36计1.JVM 管理两种类型的内存:堆内存(heap),栈内 ...