(三)数据结构

线段树

普通线段树

我的线段树 \(debug\)(记录一下做题时犯的错误):

  1. 检查 \(build\) 函数是否调用。\(\to segmentation~fault\)
  2. 区间操作 \(ql\) 是否有可能大于 \(qr\to segmentation~fault\)
  3. 结构体中元素初值问题(是否赋初值,懒标记初值是否与题目操作冲突)\(\to\) 输出可能变得极大
  4. 动态开点线段树记得建第一个点(坑死我啦 \(\to\) 什么都有可能 \(TLE,RE,segmentation~fault\)
  5. 线段树空间记得开 \(4\) 倍(经典错误)\(\to RE\)
  6. 检查各个部分的手误
  7. 检查数据范围,数组是否开小 \(\to RE\)

多刷了几道题,学习到了一些维护数据的方法。

例题 \(1.1\)区间方差 - 洛谷

这道题思路比较简单,就是将所求式子化简开来,转变成维护区间和与区间平方和即可。

评测记录 - 洛谷

例题 \(1.2\)[TJOI2018]数学计算 - 洛谷

由于模数不一定为质数,所以逆元并不一定存在,故不能直接计算。

基于操作建立线段树维护乘积,根节点保存答案。

评测记录 - 洛谷

例题 \(1.3\)[SCOI2007] 降雨量 - 洛谷

离散化后,分讨 \(x,y\) 的存在性即可,需要考虑的情况有点小多。

评测记录 - 洛谷

例题 \(1.4\)[HEOI2016/TJOI2016]排序 - 洛谷

二分答案,考虑 \(check(mid)\) 函数的写法:

观察每次排序,它会把小于 \(mid\) 的数放到一边,大于 \(mid\) 的数放到另一边,于是上线段树区间修改。

考虑将小于等于 \(mid\) 的数记为 \(0\) ,大于 \(mid\) 的数记为 \(1\)

最后模拟完所有操作后,考察第 \(q\) 位是否为 \(0\) 即可。

二分的正确性分析:若当前理想解不成立,那么比它更小的解也不可能成立,二分正确。

评测记录 - 洛谷

例题 \(1.5\)小白逛公园 - 洛谷

区间最大子段和模板,维护区间 \(lmax, rmax, sum, imax\) 转移即可。

评测记录 - 洛谷

线段树优化建图

对于如一个点向区间连边的问题,由于边数过多,建图的复杂度直接爆炸,此时可以考虑线段树优化建图。

概述

假设一个点 \(x\) 要向一个区间 \([l,r]\) 连一条边权为 \(w\) 的边,这个区间必然可以用线段树表示成不超过 \(\log n\) 的小区间。

于是,将这个点向这些小区间的代表节点(虚拟节点)连边。那么就可以使所连的边数大大减少。

此外,从这些小区间到它们的子区间应连一条边权为 \(0\) 的边,可以这样想象,能连到这个区间,必然也能连到它的子区间中的点。

例题 \(1.6\)Legacy - 洛谷

对于这道题目,它既要求由点向区间连边,也要求区间向点连边。

考虑建立两棵线段树,分别维护入边和出边,第二棵线段树需要将父区间连向子区间的边反向。

对于操作 \(1\) ,由线段树 \(2\) 的叶子结点向线段树 \(1\) 连边,操作 \(2\) 反之。

然后就是最短路模板了。

评测记录 - 洛谷

线段树的标记永久化

对于不支持懒标记下传的线段树而言,需要用到标记永久化的技巧。

标记永久化的前置条件:修改顺序不影响最后答案

如果能正常 \(pushdown\) 且满足上述条件,那么就可以进行标记永久化。

思路简述:

懒标记应该 \(pushdown\) 时,按住不下放,在最后询问的时候考虑从根节点到询问区间的所有懒标记即可。

这样说可能不明晰,看个例子:

\(2.1\)【模板】线段树 1 - 洛谷

评测记录 - 洛谷

权值线段树

顾名思义,权值线段树是维护与值域相关信息的线段树。

举个例子,权值线段树的叶子维护有几个 \(1,2\) ,它们的父亲节点维护有几个 \(1\)\(2\)

可以武断地说,权值线段树维护的是值域上各个数的个数。

应用:权值线段树最广泛的应用就是解决整个数列的第 \(k\) 大(小)的数,具体方法如下(以求第 \(k\) 小数为例):

权值线段树的父亲节点维护数的个数和,在查询时:如果左子区间的数的个数大于 \(k\) 个,那么进入左子区间,否则进入右子区间。

思路比较简单,多配合其它知识一起考察,如线段树合并,可持久化线段树等。

例题:见例题 \(4.2\) [HNOI2012]永无乡 - 洛谷

线段树合并

前置知识:动态开点线段树

线段树合并常应用于树上问题,用于合并两棵线段树之间的信息。

前置条件:能够快速合并两个叶子结点。

合并过程

合并分为两类:

  1. 合并叶子结点的信息(单点合并)

  2. 合并左右儿子的信息(区间合并)

如果当前两个节点有一个为空,\(return\)(空节点合并后信息不改变)。

否则新建一个节点,保存合并后线段树的信息。

继续递归,直到叶子结点或有一个为空,回溯时返回新建节点编号。

容易发现,合并的复杂度为两棵线段树重合的节点个数。

技巧:设被合并的线段树为 \(seg_x\)\(seg_y\) ,如果被合并的线段树 \(seg_y\) 的信息以后不会再用到,那么可以不用新建节点,而将信息直接合并到 \(seg_x\) 的节点上。

例题 \(4.1\)[Vani有约会]雨天的尾巴 /【模板】线段树合并 - 洛谷

对于每个节点建立一棵线段树,配合树上差分修改,最后向上合并线段树即可。

评测记录 - 洛谷

例题 \(4.2\)[HNOI2012]永无乡 - 洛谷

裸的线段树合并。

一个套路:并查集维护连通性,每次操作就只用在并查集的代表元素之间。

这样对于每个点开一棵权值线段树,对于每个操作合并代表元素即可。

评测记录 - 洛谷

\(4.3\)[NOIP2016 提高组] 天天爱跑步

将每次修改分两段处理,查询时查询 \(dep+w\) 的值即可。

评测记录 - 洛谷

posted @ 2022-09-11 07:40  mklzc  阅读(42)  评论(0编辑  收藏  举报