数据结构做题笔记

线段树

查找 Search
经典的想法:维护每个点合法的前驱位置,用线段树区间查找最大值是否比左端点大即可。
事实:单点修改时改变的前驱很多,无法直接维护。
优化:考虑不记录多余位置,当两个数前驱相同时,后一个数一定不会造成贡献。

富金森林公园
\(联通段数=点数-边数\)
\(k\le a_i\) 的点数都加一,\(k\le min(a_i,a_{i-1})\) 的边数都加一,模拟即可。

Hills and Pits
当且仅当区间和小于零时无解。
容易发现答案最大为 \((r-l)*2\),将整个序列扫两遍。
整个过程形如找一个起点绕到左端点,走到右端点后走到一个终点。
左端点到起点(终点到右端点)之间的线段一定走了两次。
起点与终点之间的线段走了一次或三次。
贪心的考虑中间部分,当一个点的前缀和第一次大于等于零时,一定会先把之前的坑填上。
左端点为负的线段一定被算三次,为正一定被算一次。
区间最大子段和即可。

\(trie\)

[省选联考 2020 A 卷] 树
经典,考虑求答案时异或操作可以 \(01trie\)\(pushup\)(其实直接维护根节点就行)。
将儿子进行合并,然后整体加一,再插入一个值。
\(01trie\) 整体加一,低位作为父亲,交换左右儿子即可。

并查集

白雪皑皑
倒序枚举染色,指向后面第一个没被染色的点,暴力修改即可。

[Ynoi2013] 大学
区间求和用树状数组,考虑一个数最多改变 \(O(\log V)\) 次,这一部分时间复杂度为 \(O(n \log n \log V)\)
考虑如何找到要修改的点。
将每个数放到自己的因数集合中,共 \(O(n \sum_i d(a_i))\) 个元素。
每次操作相当于将区间一段数修改,使用上一题的并查集即可。

分块

「JOISC 2016 Day 3」回转寿司
思考部分分 \(l=1,r=n\) 的情况。
我们发现只会替换掉全局的最大值(或不替换),直接大根堆维护即可。
回到原题,将序列分块,整块直接用上述方法维护,问题在如何还原散块进行暴力。
我们发现修改的顺序没有影响,只需要将所有操作的最小值与当前位置交换即可。

区间 LIS
题如其名,有 \(O(n \log^2{n})\) 的论文,但我不会。
考虑我们二分求解 \(LIS\) 的过程,我们有 \(f_i\) 表示长度为 \(i\) 的最长上升子序列结尾的最小元素。
设从 \(l\)\(r\)\(dp\) 数组中元素集合为 \(S(l,r)\)
我们发现一定有 \(S(l+1,r) \subseteq S(l,r)\)(有可能多出 \(l\) 处的元素)。
考虑枚举右端点。
因为每个元素一定是一段前缀,所以只需要知道每个元素的出现的最后位置 \(p_i\),问题就变成了全局 \(\le l\) 的数个数。
维护每个数的出现位置,考虑 \(r\)\(r+1\) 的变化。
一定有 \(p_{a(r+1)}=r+1\),只有比它大的元素被修改。
\(1\)\(r\) 第一个比 \(a(r+1)\) 大的数 \(nxt(a(r+1))\) 一定被替换,位置改为 \(0\)
下一个比 \(a(r+1)\) 大的数只有在 \(nxt(a(r+1))\) 被替换掉的时候才会被替换,位置改为 \(\min{(p_{(nxt(a(r+1)))},p_{nxt(nxt(a(r+1)))})}\)
至此,容易发现我们要做一个 「JOISC 2016 Day 3」回转寿司 即可。

动态树类问题

  1. 路径类问题考虑 \(LCT\)(暂不会)。
  2. 子树类问题考虑平衡树维护欧拉序(括号序),维护每个点左右括号编号。
    分裂出子树在平衡树上暴力跳父亲求出左右括号排名即可。
  3. 只有加点考虑根号重构(加入 \(B\) 个点后重构),配合 \(O(n)\) 初始化数据结构使用。
  4. 考虑树分块。

树的直径

动态直径
先把整棵树拍成序列,路径问题用欧拉序。
动态维护 \(\max_{i\le j}{( dep_i + dep_j - 2 \min\limits_{k=i}^{j} dep_k )}\)
可合并信息,线段树即可。

也有结论,集合 \(S \cap T\) 的直径一定在 \(S\)\(T\) 的直径四个点之中,线段树随便维护。

posted @ 2025-09-07 16:04  C_Wish  阅读(4)  评论(0)    收藏  举报