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 ...
随机推荐
- building qtqml requires python
编译Qt5.14.2的qtdeclarative发生错误:building qtqml requires python,系统是ubuntu18.04,安装有python3,修复需要做2步: 1.用na ...
- Android 12 适配之 "Android:exported"
Android 12 适配之 "Android:exported" 将 build.gradle 中的 targetSDKVersion 和 compileSdkVersion 改 ...
- win10 ocx控件注册失败的解决办法
首先注意:ocx放在大部分系统目录无法注册,比如program file ,但windows目录可以. 第一步:关闭防火墙 第二步:确定是否缺少控件运行需要的微软运行库或其他运行环境 这里提供一个挺全 ...
- Qt编写物联网管理平台40-类型种类
一.前言 为了增强本系统的拓展性,做成通用的物联网管理平台,特意将控制器主设备类型.探测器子设备类型.对应种类符号等信息,全部做成表格可自定义添加和修改,这样在控制器信息表和探测器信息表管理的时候,可 ...
- 在 Ubuntu 上搭建 MinIO 服务器
在日常开发时,如果有文件上传下载的需求(比如用户头像),但是又不想使用对象存储,那么自己搭建一个 MinIO 服务器是一个比较简单的解决方案. MinIO 是一个基于 Apache License v ...
- UML之图框架标题类型之谬
在UML中,我们可以用一个被称为"框架"的边界框围绕着UML图形,当然在很多情况下,框架可以省略,也就是不将它描画出来.但是对于某些图形类型而言,框架具有语义意义,在这些图形类型中 ...
- error C1083: 无法打开包括文件:“iostream.h”: No such file or directory
用VS2010打开VC++6程序,按下F5键会发现有错误提示:error C1083: 无法打开包括文件:"iostream.h": No such file or directo ...
- 总是被低估,从未被超越,揭秘QQ极致丝滑背后的硬核IM技术优化
本文由腾讯云开发者张曌.毕磊分享,原题"QQ 9"傻快傻快"的?!带你看看背后的技术秘密",本文进行了排版和内容优化等. 1.引言 最新发布的 QQ 9 自上线 ...
- 修改leds-gpio.c 让GPIO LED在kernel启动时就开始闪烁
内容提要: 客户需要在开机时就闪烁LED,并要求越快越好 diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index ...
- 前端学习openLayers配合vue3(获取矢量图的个数,省份的个数)
矢量图层绘制了一个中国地图,我们获取一下矢量图层的个数 关键代码 map .getLayers()//获取所有图层 .item(1)//获取矢量图层 .getSource() .on("ch ...