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 ...
随机推荐
- 痞子衡嵌入式:i.MXRT1170上PXP对CM7 TCM进行随机地址短小数据写入操作限制
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是i.MXRT1170上PXP对CM7 TCM进行随机地址短小数据写入操作限制. 在 MCU 里能够对片内外映射的存储器进行读写操作的主设 ...
- 深入理解 HTTP 协议:从基础到实践全解析
在当今数字化时代,HTTP 协议如同互联网世界的"语言",支撑着无数网页浏览.数据传输和在线交互.无论你是初涉编程的新手,还是经验丰富的开发者,深入掌握 HTTP 协议都至关重要. ...
- manim边学边做--渐变生长
本篇介绍Manim中的渐变生长类的动画. 这类动画的特点是可以清晰地展示图形的生成过程.物体的运动变化,帮助我们更好地理解抽象概念和复杂结构. 渐变生长类的动画的主要有: GrowArrow:让箭头从 ...
- Uninstall or delete MariaDB completely for re-installation
I am new to this forum so pse forgive me if I am asking a question which already has been answered. ...
- mac 10.15 国内如何安装brew
下载文件 brew_install.sh,然后执行 sh brew_install.sh 通常会卡在 tapping homebrew/core ,没关系, 执行如下命令即可 解决方法,手动执行下面 ...
- Qt/C++音视频开发60-坐标拾取/按下鼠标获取矩形区域/转换到视频源真实坐标
一.前言 通过在通道画面上拾取鼠标按下的坐标,然后鼠标移动,直到松开,根据松开的坐标和按下的坐标,绘制一个矩形区域,作为热点或者需要电子放大的区域,拿到这个坐标区域,用途非常多,可以直接将区域中的画面 ...
- Java网约车项目实战:实现抢单功能详解
在网约车项目中,抢单功能是非常关键的一部分,它决定了司机能否及时响应乘客的订单,提高整个平台的运营效率.本文将详细介绍如何使用Java来实现网约车项目的抢单功能,并提供一个完整的代码示例,以便读者能够 ...
- 在Win7 x64环境中将World Wind Java SDK 2.1.0嵌入到Eclipse中的方法
1.解压worldwind-2.1.0.zip. 2.打开Eclipse,依次点击New–>Java project ,输入project的名称WorldWind,一路直到finish.然后就可 ...
- Datawhale AI 夏令营-天池Better Synth多模态大模型数据合成挑战赛-task3持续上分(更新中)
在大数据.大模型时代,随着大模型发展,互联网数据渐尽且需大量处理标注,为新模型训练高效合成优质数据成为新兴问题."天池 Better Synth - 多模态大模型数据合成挑战赛"应 ...
- [LC952]按公因数计算最大组件大小
题目描述 给定一个由不同正整数的组成的非空数组 nums ,考虑下面的图: 有 nums.length 个节点,按从 nums[0] 到 nums[nums.length - 1] 标记: 只有当 n ...