【笔记】数据结构选讲 2024.7.31 & 2025.7.31

【笔记】数据结构选讲 2024.7.31 & 2025.7.31

同一个人讲的同一个专题,合并了。

【模板】"动态 DP"&动态树分治(树剖)

P4719 【模板】"动态 DP"&动态树分治 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

\[\begin{bmatrix} f_{u, 0}& f_{u, 0}\\ f_{u, 1}& -\infty \end{bmatrix}\times\begin{bmatrix} f_{v, 0}\\ f_{v, 1} \end{bmatrix}=\begin{bmatrix} f_{u, 0}\\ f_{u, 1} \end{bmatrix} \]

这样可以轻易写为树剖,令 \(v=son(u)\)

[Ynoi Easy Round 2020] TEST_63(LCT)

P8265 [Ynoi Easy Round 2020] TEST_63 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

用 LCT 维护重链剖分,LCT 上的虚实边直接代表重链剖分上的轻重边。

\[siz_u>2siz_v\iff \exists k, siz_u\geq 2^k>siz_v \]

维护一条链的信息(修改一条链的轻重边情况):先 access 链底全改成重边,再考虑维护树链剖分(显然根据定义只会改轻 \(O(\log n)\) 条轻边)。枚举 \(k\),在平衡树上二分找到这个 \(u\),然后去检查 \(u, v\) 是不是应该变轻。

在 LCT 上维护轻子树信息是能做的。然后你维护了重链信息之后就能做了。

维护一个集合,支持增删 \(\max\),可以开两个 priority_queue,一个存下加的数,一个存下删的数。

[JOISC 2019 Day2] Two Antennas(历史最值)

#3033. 「JOISC 2019 Day2」两个天线 - 题目 - LibreOJ (loj.ac)

扫描线,试图维护历史最大值信息。对于天线 \(i\),相当于它有一个注册时间和注销时间。对于天线 \(j\),扫到它的时候会有一个合法的 \(i\) 区间可用。维护两个序列 \(a, b\),初始都是 \(-10^9\)\(i\) 注册时,将 \(a_i\) 修改为 \(-h_i\)\(j\) 扫到时,将 \(b[]\) 区间复制为 \(h_j\),然后再推平回去。维护区间 \(a_i+b_i\) 的历史最大值的最大值。绝对值符号反过来的,reverse 再做一次。

[Ynoi2002 / CTT2022] Optimal Ordered Problem Solver(修改分类)

P9061 [Ynoi2002] Optimal Ordered Problem Solver - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

所有的点,要么没有被操作过,要么在操作矩形的并的轮廓线上。

修改:轮廓线上的点做 \(x_i\) / \(y_i\) 的区间复制,外面的点暴力查找加入。

查询:轮廓线上的点截取出来数一下,外面的三维偏序。

三维偏序是 \(O(n\log^2n)\) 的,优化是容斥,拆成上方(二维)、右方(二维)、右上角(都没被修改过,可以直接做)。

[CTT2021] 简单数据结构(修改分类)

【北大集训2021】简单数据结构 - 题目 - Universal Online Judge (uoj.ac)

如果没有 3 操作而是最后输出序列,那么可以将一堆操作合并为 \(a_i\gets \min_j(a_i+ki, v_j+t_ji)\)。后者是上凸壳,说明被修改的点是始终单调递增的。

如果已经知道每个点的首次修改时间,则可以使用线段树标记维护。为了求出前者,使用整体二分。每次整体二分求一个凸壳出来,看一眼怎么修改,然后分下去。

[Hangzhou23F] Top Cluster(直径性质)

Top Cluster - Problem - QOJ.ac

权值互不相同是关键。二分答案,查询 \(1\sim v\) 的点是否与询问点距离 \(\leq d\),取到最值的点一定是一条直径的一端。

[dmopc21c8p6] Castle Building(匹配)

[DMOPC '21 Contest 8 P6 - Castle Building - DMOJ: Modern Online Judge](https://dmoj.ca/problem/dmopc21c8p6#:~:text=Initially, 1 3 4 5 7)

分讨 \(n\) 出现的位置,改写成未出现的数匹配区间的形式,尝试使用 Hall 定理,需要对所有 \(l\leq r\) 判定一个二维的东西 \(\sum a\leq\sum b\)。全体区间可以写成两行,每一行的区间互不相交。所以我们首先对每个区间都判一次 Hall 定理。然后,考虑对于一个区间 \([L,R]\),原来是对 \([l, r]\ni[L,R]\) 有容量的贡献,考虑对 \([l, r]\in[L,R]\) 的减去这个区间的容量,这样不影响判定。于是可以转成一维的东西。修改只影响 \(O(1)\) 个区间。

[NOIWC2024] 线段树(树上 dp)

P10145 [WC2024] 线段树 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

会了,不想写题解啊

[Ynoi2009] rprmq1(历史最值)

P6109 [Ynoi2009] rprmq1 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

将询问拆成二区间合并的形式。以拆出的右区间为例,为了保证所有矩形加操作都有顾及,可以考虑在同一层中处理这些区间。用一棵历史最大值线段树扫过去,扫到一个分治区间中点的时候,为了保证查询不被中点前面部分影响,可以给全局加一个 \(+\infty\),放弃之前已经消失的最值(但是现在还在的矩形还留着),询问的时候再抠掉 \(+\infty\)。注意 \(+\infty\) 的选取。\(O(m\log^2n+q\log n)\)

[Hangzhou23K] Card Game(可持久化平衡树)

Card Game - Problem - QOJ.ac

\(t_i\) 为下一个与 \(a_i\) 相同的数的下标,那么:

\[ans(l, r)=\begin{cases} ans(l+1, r)+1, &t_l>r,\\ ans(t_l+1, r), &t_l\leq r. \end{cases} \]

\(l\) 从右往左扫,可持久化平衡树解决区间复制问题。

[Hefei23K] Campus Partition(树上 dp)

Campus Partition - Problem - QOJ.ac

所划分的一定是链,且链的两端是最大值与次大值(否则不优)。\(f_u\) 表示 \(u\) 子树内匹配完成的最大值,\(d_{u,x}\) 表示 \(u\) 子树内的点 \(x\) 要飞出去匹配。\(d_{u,x}\) 转移即为 \(=\max_{v}\{d_{v,x}+\sum_{v'\neq v}f_v\}\)\(f_u\) 转移即枚举 \(v_1, v_2\) 并取 \(d_{v_1,x}+d_{v_2,y}+\min(a_x, a_y)\) 以及其他的 \(f_{v'}\) 的和。

可以线段树合并维护 \(d_{u,x}\)。合并的时候除了维护出 \(d_{u,x}\) 以外,还要求 \(f_u\)。递归到只有一边有值的时候就可以转移 \(f_u\),向下递归时记录左右儿子的某个值的最值即可,反正线段树合并可以做。

[KOI TST 2023] 택시 여행 / Taxi(点分)

택시 여행 - Problem - QOJ.ac

运行 Dijkstra 算法。

点分树,需要支持在所有点分树父亲上插入一次函数,查询单点最小值,写若干李超线段树即可。都是平凡的。

[CTT2021] 小明的树(点边容斥)

【北大集训2021】小明的树 - 题目 - Universal Online Judge (uoj.ac)

点边容斥计算连通块,只需要分开讨论每一条边什么时候给谁有贡献,需要求 \(\sum[\text{未点亮的点-边}=1]\sum[\text{点亮的点-边}]\),写成 \(\sum[a=1]\sum b\) 算了,显然 \(a=1\) 已经是最小值,所以写为 \(\sum [a=\min a]\sum b\),线段树随机维护一下就好了。修改操作甚至不需要维护树形态,而只是 \(O(1)\) 个区间加。

[2022-2023 集训队互测 Round 2] 相等树链(点分、hash)

相等树链 - Problem - QOJ.ac

点分治到重心 \(rt\)。考虑采取异或哈希判定相等,随机权值 \(c_i\)。从 \(rt\) 出发的路径,我们要选两条拼起来,观察到这两条路径拼起来在 \(T_2\) 上的路径两端点 \(\in\) 由这两条路径各自在 \(T_2\) 上组成的路径两端点(一共四个点)的集合。枚举在 \(T_2\) 上的路径两端点分别来自哪里,使用极其恶心的树上前缀异或和的 LCA 分讨随机完成。

[CF1707E] Replace(倍增)

Replace - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

重要的观察:

\[f([l, r])=\bigcup_{i=l}^{r-1}f([i, i+1]) \]

\[f^k([l, r])=\bigcup_{i=l}^{r-1}f^k([i, i+1]) \]

这样就好多了啊!

倍增:

\[f^{2^k}([x, x+1])=\bigcup_{i=l}^{r-1}f^{2^{k-1}}([i, i+1]), \text{where }[l,r]=f^{2^{k-1}}([x, x+1]) \]

任意 RMQ 算法,没了啊!

[CF1558F] Strange Sort(0/1 转化)

Strange Sort - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

枚举 \(v=1\sim n\),将 \(\leq v\) 的标记为 \(0\)\(>v\) 的标记为 \(1\),并尝试将其排序成 \(v\)\(0\)\(n-v\)\(1\),对所有时间取最大值作为答案。考虑所有 \(1\) 的位置,从右往左记为 \(x_1, x_2, \cdots, x_k\),那么 \(x_1\) 被移到正确位置的时间为

\[t_1=n-x_i+0/1 \]

\(+0/1\) 是因为有个奇偶修正的部分。

\(t_2\)\(t_1\) 一样,不过 \(x_2\) 可能会被 \(x_1\) 挡住,但如果是这样,则 \(x_2\) 一定会紧随 \(x_1\) 之后到达它的位置。

\[t_2=\max(n-1-x_2+0/1, t_1+1) \]

\[\implies t_i=\max(n-i+1-x_i+0/1, t_{i-1}+1) \]

我们要求的是

\[t_k=\max_i(n-i+1-x_i+0/1+k-i) \]

外层的 \(v\) 减少时,这些 \(t\) 是区间修改,或者平衡树都可以做。

[QOJ4815] Flower's Land(点分、树上背包)

题解 QOJ4815【Flower's Land】 - caijianhong - 博客园 (cnblogs.com)

Segbeats(势能分析)

线段树支持 1. 区间 chkmin \(v\);2. 区间和。

考虑在线段树上每个节点维护 \(max1, max2, num, sum\) 表示最大值,严格次大值,最大值出现次数,区间和。chkmin \(v\) 的操作,分类讨论:

  1. \(v\geq max1\):无影响。
  2. \(max1>v>max2\):打上标记,表示以后将所有最大值改为 \(v\),不往下递归,\(sum\) 可以维护。
  3. \(max2\geq v\):暴力向下递归。

复杂度分析:暴力向下递归之后,这个线段树区间的不同值的个数会至少减少一,总不超过 \(O(n\log n)\)

如果加上区间加操作,复杂度变为 \(O(n\log^2n)\) 但是实际表现和 \(O(n\log n)\) 差不多,但是没人会证。听说这东西常数不小。


ABC414G AtCoder Express 4(线段树优化建图)

线段树优化建图,但是因为 Dijkstra 要求不能有负权边,所以改造线段树上的边的边权。例如从左往右的列车,左区间对应的线段树中,从下往上走时,认为是往数轴上的右边走,区间 \([l,r]\) 表示当前在 \(x_r\) 的位置,向上走的时候边权设为走到对应位置的边权;右区间对应的线段树中,从上往下走时,认为是往数轴上的左边走,区间 \([l,r]\) 表示当前在 \(x_l\) 的位置,向下走的时候边权设为走到对应位置的边权。然后线段树优化建图即可。

qoj9694 Light Drinking and Low Singing(平衡树)

01 变成 10 相当于所有 0 右移且所有 1 左移,特殊情况是边界情况和 000011111 会相撞成 0001111(长度 \(>1\) 的连续段相撞后长度各自 \(-1\))的情况。那么观察长度 \(>1\) 的连续段的长度和,每次相撞 \(-2\),边界上会 \(+O(1)\),也就是说总相撞次数为 \(O(n)\),暴力维护相撞即可。平衡树维护。

qoj9419 Normal Friends(LCT)

LCT 维护树形态。一条重链上维护:

  1. 0/1 序列表示重链上 \(i+1\) 号点是 \(i\) 号点的左 / 右儿子。
  2. 所有 0 的标号序列,以及它的翻转 tag。
  3. 所有 1 的标号序列,以及它的翻转 tag。

Access 操作时:需要对这三个序列进行 split / merge 操作,故使用无旋 Treap 维护序列。

Tag 的下放:tag 表示将整棵子树的左右儿子翻转,需要 flip 第一个序列,swap 第二、三个序列,并对第二、三个序列继续打 tag(Tag 很可能要放到无旋 Treap 上进行维护)。

询问就 access 并打 tag 就行了。

Gym-105667C MIT Tour(树剖线段树)

标准题。按深度进行 dp。可以点分治。可以树剖线段树,意思就是,假如上一层是 \(v\),下一层是 \(u\),则考虑它们的 LCA,\(u\) 去枚举 LCA 在它头上的哪条重链上,然后分类讨论,看看 \(v\) 跳到这条重链上时,是在 \(u\) 下方还是 \(u\) 上方,由此得到两种转移。然后用线段树优化一下就可以了。

qoj2064 Bitset Master(点分治)

难绷:【20省选集训day1】Bitset Master | Zhengrui Online Judge

点分治。先求出 \(u\) 第一次走到根的时刻,这个可以以边为对象进行 dp。再求出 \(u\) 走到其它子树的时刻,也就是多次询问(离线)第 \(i\) 时刻从根出发走到别的子树去,在 \(q\) 时刻之前能走到多少个点。可以从小到大枚举 \(i\),每次增大时,暴力向下 dfs 去更新每条边经过的时刻。

无标题(树剖、树上背包)

dfs 时做背包。\(dp_{u,sa,S}\) 表示当前在 \(u\) 点,\(\sum a_i=sa\)\(u\) 到根路径上每个点选或不选的状态记为 \(S\)。然后像上文 Flower's Land 一题的方法进行背包。优势是复杂度中 \(k\) 的指数仅为 \(1\)

为了击杀 \(2^n\),我们树剖,先遍历轻儿子再递归重儿子,并规定:仅在我们可以确定一个点的选或不选状态时,将它从 \(S\) 中击杀。这样 \(S\) 中只会剩下 \(O(\log n)\) 个点,也就是到根路径的所有轻儿子父亲(为什么???)。复杂度 \(O(n^3k)\)

[WC2018] 通道(边分治、虚树、直径)

【笔记】树论选讲 2023.12.14 - caijianhong - 博客园

qoj6350 MIT(一般图的匹配、重心)

结论:匹配点集是单调的。可以用费用流考虑二分图的情况。

\(k=n/2\) 时,匹配点集是所有点。匹配的具体方案可以选出树的重心,这样所有点都能通过重心两两匹配了。答案就是所有点到重心的距离的和。

\(k\) 减少时,我们删除离当前重心最近的点,并移动重心。将重心移动到最大的子树(如果它的大小 \(>siz_{rt}/2\))。如何判断当前重心是否合法呢?先判断它头上的子树合不合法,再判断它子树中的中位点所在子树是否合法,即可。

无标题(边分治、Boruvka)

有一棵树,边带整数权。求一个完全图的最小生成树,\(u,v\) 的边权为 \(|dist(u,v)|\)

边分治,每层内求出只考虑 \(u\in S, v\in T\) 的边的最小生成树,并保留它们,最后剩下 \(O(n\log n)\) 条边,求最小生成树。

边分治一层里面,我们可以用 Boruvka 算法,用双指针求出最近的点。

posted @ 2024-07-31 08:30  caijianhong  阅读(367)  评论(0)    收藏  举报