1.5 树上数据结构

\({\Large 约定}\)

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

problems

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

P5903 【模板】树上 K 级祖先

\(\textcolor{blue}{\mathrm{提高+/省选−}}\) tag 树链剖分

长链剖分板子题,不过感觉更像用长链剖分进行的构造。
考虑到用倍增的复杂度是 \(\mathcal{O(\log n)}\) 的,显然我们接受不了。考虑到我们可以预处理一下某个结点的 \(k\) 级祖先,但是存哪些结点的 \(k\) 级祖先?\(k\) 是多少?这时候就要长链剖分了。
类似重链剖分,我们规定重儿子是子树中深度最大的儿子,那么这个重儿子跟这个结点在同一条链上。为了跟 \(k\) 级祖先更接近并且往上跳更少的步数,我们可以先跳到 \(x\)\(2^t\) 级祖先 \(y\) 使得 \(2^t \leq k < 2^{t+1}\)。再跳到 \(y\) 所在链的顶端,现在有两种情况:\(y\) 所在链的链顶是 \(k\) 级祖先的祖先,\(y\) 所在链的链顶是 \(k\) 级祖先的子节点。这里有一个比较重要的性质:\(x\)\(k\) 级祖先与 \(y\) 所在链的链顶之间的距离不超过 \(y\) 所在链的链长。接下来来证明。
\(d\)\(y\)\(x\)\(k\) 级祖先的距离,\(len\)\(y\) 所在链的长度。

\[\because 2^t \leq k < 2^{t+1} \\ \therefore d \leq 2^t-1 \\ 根据长链剖分的定义,len\geq 2^t \\ \therefore d \leq len \\ Q.E.D. \]

那么我们就可以直接存储一下链顶往上和往下链长以内的点是什么即可。

submission

2025.12.29

CF375D Tree and Queries

2400 tag 数据结构 DFS

树上启发式合并……感觉还是比较显然的。
显然可以把询问挂到每一个结点上,这样我们可以在做完一个子树的贡献时直接把答案加进去。然后想怎么样启发式合并。我们用一个 \(cnt\) 数组记录当前每种颜色出现的次数,用 \(sum\) 数组记录一下有多少种颜色出现的次数不少于 \(i\)。当我们加入一个数的时候,只需要将这个数现在出现的次数在 \(cnt\)\(+1\),这是显然的。删去这个数同理,答案就是 \(cnt\)
感觉启发式合并的写法还是挺多的,不过为啥我看不懂啊。/kk

submission

2025.12.30

P4211 [LNOI2014] LCA

\(\textcolor{purple}{\mathrm{省选/NOI−}}\) tag 树链剖分 LCA 差分 离线

感觉好神奇啊。
题目要我们求 \(\sum_{i=l}^r dep[\operatorname{LCA}(i,z)]\)。我们先考虑要怎么求 \(\operatorname{LCA}\) 的深度。我们可以把 \(\operatorname{LCA}\) 都求出来再累加深度,但显然当区间范围比较大的时候时间复杂度是接受不了的。
还有另一种做法。因为 \(\operatorname{LCA}\) 肯定是 \(z\)\(i\) 的祖先,所以我们可以把它们到根的路径都加上 \(1\)。要求深度时我们只需要求这个点到根的路径和即可。因为两个点到根的路径会在 \(\operatorname{LCA}\) 处重合,那么 \(\operatorname{LCA}\) 到根的这段路程会被算进去,显然段路程的和就是深度。对于多个点也是同样道理。这些操作用重链剖分去做还是比较方便的,但是我们发现比较在求和时难删去不需要的贡献。如果说每次询问都做一遍的话复杂度还是很高。但如果固定了一个询问路径,是可以通过区间差分的方式将多余的贡献删除的。
考虑将一个询问拆分成 \(\sum_{i=1}^r dep[\operatorname{LCA}(i,z)]-\sum_{i=1}^{l-1} dep[\operatorname{LCA}(i,z)]\),我们发现这样左端点是固定的,只需要在每次加入点的时候计算一下贡献就好了。

submission

2025.12.30

PP3806 【模板】点分治

\(\textcolor{blue}{\mathrm{提高+/省选−}}\) tag 点分治

假如说有一个联通块,考虑如何计算这个联通块的路径。从中找出一个点,这样这个联通块的路径就能分成两类:经过这个点的和没有经过这个点的。
经过这个点的显然可以从这个点出发遍历整个联通块,计算出这个点到其他点的路径长度,然后就可以把每个儿子的子树情况合并一下,用一个桶标记一下就可以了。当然做每一个联通块的时候都要清空。
没有经过这个点的情况显然可以把大联通块分裂成小联通块继续做。
每次选重心是最好的,这样分裂最多进行 \(\mathcal{O(\log n)}\) 的。

submission

2025.12.31

P4178 Tree

\(\textcolor{blue}{\mathrm{提高+/省选−}}\) tag 点分治 树状数组

与上面那道题不太一样的是,这里要求的是 \(\leq k\) 的。只需要将桶变成树状数组求前缀和即可。

submission

2025.12.31

#763. 树哈希

tag 树哈希

树哈希模板题。
假如说有一棵有根树,显然一个结点的哈希值是可以通过它的子节点计算得到的。
我们可以用 \(xor hash\) 映射出当前结点子节点的哈希值,然后把子节点的哈希值都加起来。为了预防出题人对着 \(xor hash\) 卡,还可以在映射前后异或一个随机常数。
判断不同的树的种类可以把子树根节点的 \(hash\) 值扔到一个 \(set\) 里即可。

submission

2025.12.31

P5043 【模板】树同构 / [BJOI2015] 树的同构

\(\textcolor{blue}{\mathrm{提高+/省选−}}\) tag 树哈希 动态规划

上面那题略作修改。
这是无根树所以无法直接比较树的哈希。
但是显然可以把以每个点为根的树的哈希都算一遍再求出这些值的哈希值,这样就可以判断树的同构了。求哈希值可以用换根 \(dp\) 去做。

submission

2025.12.31

posted @ 2025-12-29 14:13  yqfff_qwq  阅读(12)  评论(0)    收藏  举报