1.1 线段树平衡树进阶

\({\Large 约定}\)

  1. 用集合符号表示位运算符号,用 $ \oplus $ 表示异或,特别的,$ i \in S$ 表示二进制数 \(S\) 的第 \(i\) 位为 \(1\)
  2. \(V\) 表示值域,\(\sum\) 表示字符集,\(\omega\) 表示 bitset 的常数 \((\omega = 64)\)
  3. 除去用 \(()/[]\) 表示开闭区间外,\([]\) 仅表示艾弗森约定,\(\{\}\) 仅表示集合,括号嵌套全用 \(()\)
  4. 字符串或序列角标为区间表示对应区间的子串

problems

\(\textcolor{white}{\mathrm{pw:HElloWOrLd}}\)

P4556 【模板】线段树合并 / [Vani有约会] 雨天的尾巴

\(\textcolor{purple}{\mathrm{省选/NOI−}}\) tag 线段树 线段树合并

既然是线段树合并模板题那就思考一下要怎么运用。我们可以对每个房屋都建一棵线段树,叶子结点表示对应种类的救济粮的数量,显然这个可以 push_up 合并贡献。
一次操对于修改和查询,考虑树上差分。那么 \(x\) 结点的线段树就是 \(x\) 所在子树中所有点的线段树合并后的结果。
线段树如何合并呢?考虑像可持久化线段树那样,考虑对应位置上的两个点,把这两个点合并起来就好了。

submission

2025.12.15

P5494 【模板】线段树分裂

\(\textcolor{purple}{\mathrm{省选/NOI−}}\) tag 线段树

先考虑每个操作是怎么样的。
\([x,y]\) 分裂出来:像 FHQ 一样,先把 \([1,n]\) 分成 \([1,x-1]\)\([x,y]\) 以及 \([y+1,n]\),最后把 \([1,x-1]\)\([y+1,n]\) 合并就好了,复杂度应该是 \(\mathcal{O}(\log n)\) 的。
\(t\) 树合并到 \(p\) 树:运用线段树合并即可。
\(p\) 树中插入 \(a\)\(q\):单点修改即可
查询 \([x,y]\) 之间数的个数:区间求和
查询第 \(k\) 小:权值线段树的操作即可。
合并时间复杂度的证明不是很会QAQ。
线段树如何分裂呢?如果说当前节点对应的区间包含在你想要分裂出来的区间中,那么就把这个节点加到新增的线段树中,并在原来的线段树中删去这个节点。
当然好像还有类似 FHQ 思路的分裂?

submission

2025.12.15

P2824 [HEOI2016/TJOI2016] 排序

\(\textcolor{blue}{\mathrm{提高+/省选−}}\) tag 线段树 二分 排序

这里可能要用到 trick 里的第一个 trick。
发现对于任意一个 \(x\),排完序之后一定是一段 \(\leq x\) 的前缀和一段 \(> x\) 的后缀,将 \(\leq x\) 的数视作 \(0\)\(> x\) 的数视作 \(1\)。称对于 \(x\) 已经满足排成一段 \(\leq x\) 的前缀和一段 \(> x\) 的后缀为 \(x\) 有序。
假设我们二分的答案为 \(p\),我们设 \(b_i=[a_i\geq p]\)。一次排序操作即为将 \(b\) 中某段区间中的所有 \(1\) 都移到前面或后面。这个显然可以用线段树维护。最后我们求出所求位置是 \(0\) 还是 \(1\)。如果是 \(0\) 的话说明这个数偏小了,所以 \(l=mid+1\),否则 \(ans=mid,r=mid-1\)
好像还有一种用 set+ 线段树合并的 \(\mathcal{O}(n\log n)\) 的做法?但是我可能不会做QAQ。

submission

2025.12.15

P4314 CPU 监控

\(\textcolor{purple}{\mathrm{省选/NOI−}}\) tag 线段树

考验对线段树 push_down 的理解。
我们可以把对每个点的操作看成一个操作序列。显然从父节点传下来的操作一定在子节点原本的操作之后。那么我们的操作序列可以简化成:加操作+覆盖操作+(加操作)
假如说不需要查询历史区间最大值,这个是简单的。
一个数 \(x\)\(y\) 之后对历史最大值的影响是 \(z=\max(z,x+y)\)
一个数 \(x\) 覆盖成 \(y\) 之后对历史最大值的影响是 \(z=\max(z,y)\)
我们记录四个 tag:sum、val、max_sum、max_val。
分别表示:当前加操作的 tag 和,当前覆盖操作的 tag,sum 最大是多少,val最大是多少。
要如何 push_down 呢?先下放加法的标记,再下放覆盖的标记。
加法标记如何修改?假如说上一次是加法那这一次显然也可以直接加到加法中,否则加到覆盖中去当成覆盖看。同样,如果上一次是覆盖的话也可以直接覆盖,否则直接更新历史区间最大覆盖值和答案即可。

submission

2025.12.16

#228. 基础数据结构练习题

线段树 数学

显然,如果没有加 \(x\) 的操作,这道题就很简单了。
如果说一个区间内的所有数都相等,那么显然可以用区间减代替。
我们发现开方只是起到了一个缩小最大值和最小值差值的作用,当差值缩小为0时就是我们所想要的那种情况。
但会有特殊的情况,比如 \(898989\) 变成 \(343434\),这种情况下可以发现这些数变化的差值都是相同的,那么显然可以区间减去做。可以发现在维持区间极差不变的只有 \(0\)\(1\) 两种。这里修改可以直接标记永久化,但是感觉细节有点多。

复杂度不太会证明QAQ。

submission

2025.12.16

P4097 【模板】李超线段树 / [HEOI2013] Segment

\(\textcolor{purple}{\mathrm{省选/NOI−}}\) tag 李超线段树 贪心

我们假设 \(tr_i\) 表示线段树上 \(tr_i\) 这个结点所对应区间的中点的最大线段编号。那么我们就递归下去,如果说所求点被包含在这个区间中,那么就让 \(ans\) 和这个区间比一下大小即可。修改操作的话需要我们递归下去,如果说当前这段区间被完整覆盖在大区间中,那么就去修改它。
要怎么修改呢?
假设当前处理的是结点 \(i\),先比较一下 \(tr_i\)\(x\)\(mid\) 时的大小,如果 \(x\) 更大的话就先交换 \(x\)\(tr_i\)。接下来比较 \(tr_i\)\(x\)\(l\)\(r\) 时的大小关系。如果说在 \(l\) 时更大或相等且序号更小,那么修改左半边即可。在 \(r\) 时同理。

submission

2025.12.16

CF932F Escape Through Leaf

2700 tag 动态规划 数据结构

李超线段树合并的应用。
我们有最朴素的 \(dp\)\(dp_x=\min(dp_y+a_x\times b_y)(y\in subtree(x))\)
如果我们将 \(b_y\) 看成斜率, \(dp_y\) 看成纵截距, \(a_x\) 看成横坐标, 那么问题转为了在平面上有一些直线, 选出与直线 \(x=a_x\) 相交的最靠下的点。这个显然可以用李超线段树维护。但是如果为每个结点都建一棵李超线段树的话显然在时空方面都是不行的,那么我们可以用线段树合并的想法,这样就可以把子树中的信息合并到一棵上了。要注意有负数,所以要向右移。

submission

2025.12.17

P4198 楼房重建

\(\textcolor{purple}{\mathrm{省选/NOI−}}\) tag 线段树 递归

\(\mathcal{O(\log n)}\) 的 push_up
我们记录两个东西:当前区间的答案 \(s_i\) 和当前区间的最大值 \(d_i\),很显然一个结点的状态是由两个子节点更新的,现在考虑怎么更新。
如果说前面最大斜率 \(<\) 当前区间第一个楼的斜率,那么直接返回 \(s_i\)
如果说前面最大斜率 \(\geq\) 当前区间最大斜率,那么直接返回 \(0\)
如果 \(l=r\),那么就比较当前楼的斜率和前面最大的斜率。
如果左子树中的最大斜率 \(\leq\) 前面最大斜率,访问右子树即可。
如果左子树中的最大斜率 \(>\) 前面最大斜率,访问左子树并加上右子树的答案。

submission

2025 12.17

P9285 [AGM 2023 资格赛] YsaeSort

\(\textcolor{purple}{\mathrm{省选/NOI−}}\) tag 数据结构 暴力

我好像只会 \(\mathcal{O(n^2)}\) 的暴力QAQ。
我们可以把每个数看成一个有序的区间,一次排序操作会将多个有序的区间合并成一个新的有序的区间。
因为保证 \(1\) 操作排序的区间两两不交或包含,所以最终总的段数是越来越少的。
我们可以用 set 维护有序区间的信息,set 中的两个点就表示一个左闭右开的区间。
一开始我们先给每个位置都插入这个位置上的数,再把这些位置以及 \(n+1\) 都插入到一个大的 set 中。
合并的操作还是比较容易理解的,只要找到未合并的左端点把这个点对应的 set 合并,然后再把中间区间的左端点都删除即可。
然后考虑怎么优化,因为每次操作 \(1\) 的排序保证区间包含或不交,所以每次相当于合并若干个有序段,这部分可以 \(\mathcal{O(n\log^2 n)}\) 启发式合并 set,然后再把当前这个区间的 set 遍历一边放回原序列。
我们发现合并都是有序区间的合并,那么我们就可以用归并排序 \(\mathcal{O(n)}\) 的方式进行合并了。观察到上面这种情况每次合并的有序段不会很多,所以我们考虑当有序段个数小于一个很小的常数 \(B\) 时,做 \(B\) 次归并排序。取 \(B=3\) 就能通过这个题了。
答案显然是好求的。
想要学习 \(\mathcal{O(n\log^2 n)}\) 的正解的可以看lynkcat大神的这篇题解。

submission

2025 12.18

posted @ 2025-12-15 08:53  mark0575  阅读(5)  评论(0)    收藏  举报