jry 线段树学习笔记
一些有用的资料
区间最值操作 的 时间复杂度证明
首先规定整棵线段树被称为
接着定义函数
定义函数
我们再定义一个概念:对于一个线段树节点,如果它对应的区间包含于操作区间
以下证明正式开始:
首先对于操作区间
可能有人会疑惑,不是
对于当前递归到的节点
-
递归停止,即
或 。此种情况, 不作变化。 -
递归继续,即
。此种情况, 必然至少减小 ( 和 都被置为了 )。
显然情况
证毕。
对于 作用的理解
一方面,
另一方面,
算了,越说越玄。
感觉 jry 在想到这个 idea 的时候单纯是为了方便打懒标记,然后发现出来的算法复杂度恰好正确。
教练语:
为什么不直接使用 作为势函数?
势函数即用来得到时间复杂度的一个辅助函数,如刚刚的
。
因为在从“线段树区间节点”继续向下递归的过程中,我们不能保证
带加减操作 的 时间复杂度证明
不会。
区间历史最值 为什么一定要将 操作转化为 操作
若不这么转化的话,就需要开两个
此时,将一个节点的
但是
举个例子,该节点先与
所以,将两种操作转化为一种操作,就不会有顺序问题了。
关于实现
主要是要维护四个懒标记。
struct SegmentTree{
int l, r;
ll sum, mx, se, cnt, mxb, addam, addas, addbm, addbs;//开 ll 原因:se 可能要炸
#define l(x) tree[x].l
#define r(x) tree[x].r
#define mx(x) tree[x].mx
#define se(x) tree[x].se
#define cnt(x) tree[x].cnt//最大值的数量
#define sum(x) tree[x].sum
#define mxb(x) tree[x].mxb
#define len(x) (r(pt)-l(pt)+1)
#define addam(x) tree[x].addam//作用于最大值的加减标记
#define addas(x) tree[x].addas//作用于其它数的加减标记
#define addbm(x) tree[x].addbm//最大值加减标记的历史最大值
#define addbs(x) tree[x].addbs//其它数加减标记的历史最大值
//为什么要维护其它数加减标记的历史最大值?
//因为若不这么做,假设左区间的最大值为当前区间的最大值,而右区间的最大值不是
//那么左区间可以正常更新 mxb,而右区间则无法更新到正确的 mxb
} tree[MAXN*4];
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下