【学习笔记】数据结构
这边放一些不知道放哪里的数据结构。
1.左偏树(可并堆)
普通堆的合并复杂度为
当然右偏也是没有一点问题的。
- dist 的定义
对于二叉树,定义外节点为仅有一个儿子的节点,那么外节点
一棵大小为
证明:
-
令根节点为
,易得一棵 的二叉树至少有 层是满的。 -
那么至少有
个点。转换一下就是上面的东西了。
- 左偏树
左偏树是一棵二叉树,令左儿子为
这个很容易维护,发现左右儿子不平衡交换一下就好。同时父节点的
加入一个数可以看做进行一个合并,所以最主要的操作就是如何维护堆的合并了。
合并时首先要保证堆的性质,按小根堆讨论,把更小的当作堆顶,然后把这个根的右儿子和另一个堆合并。然后合并完之后把不满足左偏性质的调一下位置。
因为合并时保证了堆的性质所以正确性是有的。
- 复杂度证明:
因为左偏性质,每一次往下递归都会使
- 删除
删根直接合并它的左右儿子。
删除任意点就把自己的左右儿子合并然后看父节点有没有需要调整(
注意左偏树的深度 没有保证,上面删任意点的复杂度是
然后左偏树是能够支持 打 tag 的,删根或者合并的时候下传就好。
有一个原理和左偏树差不多的搞法叫随机堆,每次合并的时候用随机数决定要不要换左右儿子,平均下来复杂度差不多也是
- 可持久化可并堆
或者直接说 k 短路 算了。
用可并堆的复杂度在正边权的情况下是
因为求 k 短路,肯定和最短路要扯上关系,那么先在 反图 上跑一个 dijk 得到每个点
然后我们考虑原图上的一个路径现在怎么表示:可以用一个边集
此时我们就能够把所有可行路径的长度表示出来了,接下来我们考虑如何寻找解。可以考虑将边集中的最后一条边换掉,也可以新加一条边。显然这样是能够得到所有解的。因为边集有序,我们换的边的起点一定是最后一条边的终点能够直接或间接到达的点。直接到的点非常好处理,间接到达的点分为两种,一种是沿着最短路树往上面走然后在一个点跳出去,也就是说当前点可选的边集中一定包含所有其最短路树上的祖先的边集。那么我们就需要支持类似边集的复制合并。另一种是沿着非树边往外面跳,至少经过两条非树边,很容易发现每多经过一条非树边,路径长度一定变大,那么这种情况根本不可能成为局部的最优解,也就完全没有必要考虑。至于新增一条边就可以直接在最短路树上找个祖先跳就好了。
重新分析一下我们要干什么,对于一个点代表的能更新的边集,它包含所有祖先的边集,且祖先边集仍然有用,然后要在这些边集中找到总贡献最小的更新答案。也就是说我们需要支持两个堆的合并,并且合并后被合并的堆仍然存在。考虑可持久化左偏树。加上可持久化其实很简单,就是原本合并直接合到节点上,现在我们新开一个点合并就能够保证被合并的堆不被打乱。
2.笛卡尔树
不是很常用。其实就是实现单调栈的,但是分治性质更好。
对于每个节点有两个键值
在
关于复杂度,我们使用栈来维护这个右链,那么找最后一个就是从后往前跳,然后找完之后经过的东西都应该不在右链上了,所以每个元素只会进栈出栈各一次,复杂度为线性。
3.珂朵莉树
其实是对 STL-set 的扩展运用,一般用于解决数据随机带有推平性质的题目。
首先它的复杂度基于数据的随机性,可以 证明 大概是一个
思想及其简单,就是把序列里面值相同的值合起来组成一个三元组
首先是唯一的核心操作 split(p)
,把一个整体拆分成两份,目的是为了把
set 中 lower_bound
的作用是找到第一个大于等于的位置,在此处对应包含它的区间的后面一个区间。如果这个不是 set 自带的结尾并且第一个就是 insert({p,r,v}).first
即插入后的迭代器返回去,某些时候可以省很多事。
然后是推平操作,这个真的超级简单,我们将要推平的区间先表示出来,即拆出来一个以 split(r+1)
,原因是先分割左边可能导致后面的区间出现变动导致前面返回的迭代器失效,此时再用前面的迭代器就会出现奇奇怪怪的错误。然后做完之后我们 erase(lit,rit)
再进行一次插入就好。erase
这个函数能够把
剩下的所有所有东西都是基于这个东西的暴力,每段每段做就好了,复杂度参考上面给的链接。
4.K-D Tree
维护多维空间内的点,用起来比较类似线段树。基于数据随机的情况下复杂度为
利用替罪羊树思想进行维护。每一层按照不同维度交替将空间分割,对于一个节点 nth_element
实现。
遇到多个位置相同的点询问允许的情况下可以看做不一样的点,节省很多空间和码量。
遇到离线题可以考虑把所有点先建出来然后考虑打一个激活标记代表一个点现在存不存在(如果有插入删除的话),可以很大程度上优化替罪羊的重构。
5.树套树
5.1 树状数组套线段树
以该问题举例:单点修改,每次查询一段区间值域在限定区域内的数个数。
对于每一个树状数组节点开一棵动态开点线段树,保存其根节点。查询修改时按照树状数组原本的查询方式查询即可。
5.2 树状数组套主席树
举例:单点修改,每次查询一段区间内第
对于每一个树状数组开一棵动态开点线段树,保存其根节点。查询时把对应的
与上面内容不同的原因是区间第
5.3 另一种动态区间 kth 问题解法
外层值域线段树,内层区间线段树。
查询的时候直接在值域线段树上面二分。
修改的时候原来的值把当前位置删去,然后在修改后的值那里把当前位置加上。
内层是线段树外层根本 pushup 不了所以要边走边直接更新即舍弃 pushup。
这个空间是
直接把里面的区间线段树变成 平衡树。因为对于内层数据结构我们查询的东西只有值域在
然后你删除一个点对应加一个点所以你直接回收节点做到真正的单
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具