图论(树)笔记
图论(树)讲解部分
随机树据
在题目中树为纯随机生成时,会有一些额外的性质:
- 当第 \(i\) 个点的父亲在 \([1, i − 1]\) 中等概率随机选取,那么树的高度期望为\(O(log n)\)。
- 当树从所有有标号无根树中随机选取,那么树的高度期望为 \(O(\sqrt{n})\)。
树上倍增
倍增在关于树的问题中是一种非常常用的技巧,特别是在刻画树上路径时。
倍增本质上是对一个点到根形成的序列的某个子列的刻画,并不局限于 \(2^k\)
级父亲。同时因此,倍增在最深公共祖先问题中在某些方面比其他算法更优秀。
例题1:
题面
给定一棵树,初始时只有根一个点,你需要维护如下操作:
- 给定 \(u\),假如现在树上有 \(n\) 个节点,那么新增一个父亲为 \(u\),编号为 \(n + 1\)的叶子节点。
- 给定 \(u, v\),询问这两个的 lca。
强制在线,操作共 \(n\) 次。
\(1 \le n \le 3 \times 105\)
思路
我们尝试使用倍增来求 lca,注意到加入节点 \(i\) 时,所有 \(i\) 的祖先的倍增数
组已经求出,我们可以直接利用来求出 \(i\) 的倍增数组。所以加入一个叶子的复杂度就是 \(O(log n)\)。
总复杂度 \(O(n log n)\)。
[省选联考 2021 A/B 卷] 宝石
题面
给定 \(n\) 个点的树,每个节点有一种宝石,第 \(i\) 个节点宝石种类为 \(w_i\),宝石
总共有 \(m\) 种。
你有一个宝石收集器。这个宝石收集器能按照顺序收集至多 \(c\) 颗宝石,其
收集宝石的顺序为:\(P_1, P_2 \dots P_c\)。更具体地,收集器需要先放入第 \(P_1\) 种宝
石,然后才能再放入第 \(P_2\) 种宝石,之后再能放入第 \(P_3\) 种宝石,以此类推。其中 \(P_1, P_2 \dots P_c\) 互不相等。
你到达一个点后,如果该点上宝石种类和当前收集器中需要放入的种类相同,则你可以把一个该种宝石放进收集器。
询问 \(q\) 次,每次给出起点 \(s_i\) 与终点 \(t_i\),你需要回答,如果你走从 \(s_i\) 到 \(t_i\)的最短路,收集器中最多能收集到几
个宝石?(在每次询问中,收集器内初始时没有任何宝石。起点与终点城市集市上的宝石可以尝试被收集)
\(1 \le n, q \le 2 \times 105,1 \le c \le m \le 5 × 104,1 \le wi \le m\)。
思路
考虑二分答案 \(x\),如何判定。记 \(l = lca(s_i, t_i)\),把路线拆成 \(s_i\) 到 \(l\),\(l\) 到 \(t_i\)两部分。
对于宝石种类为 \(P_i\) 的点 \(u\),记 \(f_u,0\) 为它向上第一个种类为 \(P_i+1\) 的点,
\(g_u,0\) 为它向上第一个种类为 \(P_i−1\) 的点,然后记\(f_{u,j} = f_{fu,j−1,j−1, gu,j }= g_{gu,j−1,j−1}\)。
令 \(U\) 为 \(s_i\) 向上第一个种类为 \(P_1\) 的点(包括自己),\(V\) 为 \(t_i\) 向上第一个种类为 \(P_x\) 的点(包括自己),那么就利用 $f, g $倍增判定 \(x\) 是否可行即可。
复杂度 \(O((n + q)log_2n)\)。
树上差分
当需要对链进行操作,可以离线的时候,树上差分非常好用。
树上差分本质上是维护父亲减去所有儿子的差分数组,使得对链修改时只需要改 \(O(1)\) 个点,还原时也可以 \(O(n)\) 全部还原。
[NOIP2015 提高组] 运输计划
题面
给定一个点数为 \(n\) 的带非负边权的树,以及树上的 \(m\) 条链(可能有交),一条链的长度为链上所有边的边权和。
你现在可以把恰好一条边的边权变为 \(0\),你需要使得这 \(m\) 条链的长度的最大值最小。
\(1 \le n, m \le 3 \times 105\)。
思路
我们可以二分答案 \(x\),对于链长已经小于等于 \(x\) 的链,我们可以忽略,那么我们只需要求出一条边权最大的边,使得剩余所有链都包含这条边。
我们预处理 lca 后,就可以用树上差分在 \(O(n + m)\) 的复杂度内求出每条边被多少条链包含。
复杂度 \(O((n + m)log n + (n + m)log W)\),其中 \(W\) 为边权和。
LCA技巧
考虑对于有根树而言,求 \(dep_{lca(u,v)}\) 可以视为,对 \(u\) 到根的路径链加一,然后求 \(v\) 到根路径上的和。
[LNOI2014] LCA
题面
给出一个 \(n\) 个节点的有根树。一个点的深度定义为这个节点到根的距离 \(+1\)。
设 \(dep[i]\) 表示点 \(i\) 的深度,\(LCA(i, j)\) 表示 \(i\) 与 \(j\) 的最近公共祖先。
有 \(m\) 次询问,每次询问给出 \(l, r, z\)。
\(\sum_{i=l}^r dep[LCA(i,z)]\)。
\(1 \le n, m \le 50000。\)
思路
离线,将\(\sum_{i=l}^r dep[LCA(i,z)] 拆成\sum_{i=1}^r dep[LCA(i,j)]和\sum_{i=1}^{l-1} dep[LCA(i,z)]\)。
\(\sum_{i=1}^r dep[LCA(i,j)]\),相当于令\(\forall 1 \le i \le r\) ,到根的路径加一,然后询问\(z\)到根的链和。对询问排序后重链剖分即可。
时间复杂度 \(O((n + q)log^2n)\)。
长链剖分
在题目与树的深度/链长/直径有关的时候,长链剖分是一种常见的技巧。
特别是在 dp 状态涉及子树深度时,长剖可以用来优化 dp。
长链剖分和重链剖分类似,设子树深度最深的儿子作为长儿子,其他为短儿子,长儿子对应的边为长边,连通的长边构成长链。可以证
明,从任意点到根节点至多经过 \(O(\sqrt{n})\) 条长链。
同时,由于长链的性质,可以很快地解决一些和深度有关的问题,比如
\(O(n log n) − O(1)\) 求 \(k\) 级祖先
[POI2014]Hotel 加强版
题面
给定大小为 \(n\) 的树,边权全为 \(1\),求三元组 \((i, j, k)\) 的数量使得\(1 \le i < j < k \le n\) 且 \(i, j, k\) 在树上两两的距离完全相同。
\(1 \le n \le 105\)。
思路
令 \(f_{u,j}\) 为 \(u\) 子树内离 \(u\) 距离恰为 \(j\) 的点的数量。
令 \(g_u,j\) 为 \(u\) 子树内,满足如下条件的点对 \(v, w\) 的数量:令 $v, w $ 的 lca 为
\(l\),那么 \(v, w\) 离 \(l\) 的距离相等且 \(l\) 离 \(u\) 的距离恰为 \(v\) 离 \(l\) 的距离减 \(j\)。
写出转移式子,当加入 \(u\) 的儿子 \(v\) 时:
- \(ans ← g_{v,j}f_{u,j−1} + g_{u,j}f_{v,j−1}\)
- \(g_{u,j+1} ← f_{u,j+1}f_{v,j}\)
- \(f_{u,j+1} ← f_{v,j}\)
- \(g_{u,j−1} ← g_{v,j}\)
注意到,如果暴力转移,那么复杂度是 \(O(len_v)\),其中 \(len_v\) 为 \(v\) 子树的深
度。同时,从长儿子继承状态是 \(O(1)\) 的。所以总复杂度是所有长链的长度和
即为 \(O(n)\)。
树的重心
直径和重心都是树非常重要的特征。重心的定义为使得删去后剩余子树最大点数最小的点,但是它有其他更有用的定义。
[ZJOI2015] 幻想乡战略游戏
题面
给定 \(n\) 个点的无根树,点有非负点权 \(d_i\),边有正边权。有 \(Q\) 次操作:
修改某个 \(d_i\),保证修改后 \(d_i\) 非负。
查询:$$min_u{\sum_{i=1}^{n} dis(u,i) d_i}$$
其中 $ dis(u, v)$ 为 \(u, v\) 两个点在树上的最短距离。
\(1 \le n, Q \le 105\)。
原题作为动态点分树板子还有个每个点度数不超过 \(20\) 的限制,但是我们有一个更简单的做法不需要这个限制。
思路
事实上,使得要求式子最小的点 \(u\) 即为树的带权重心,这也是重心的其中一个定义。
我们令 \(1\) 作为根,求出每个子树点权和 \(sz_i\),那么重心就是满足$sz_1 \le 2sz_u $且使得 szu 最小的 u,这是重心的另一个定义。
那么假如我们已经获得了一个重心子树内的点 \(v\),我们只需要不断令\(v ← fa_v\),直到 \(2szv \ge sz1\) 即可找到重心,这个过程我们可以用树状数组/线段
树维护子树的点权和,配合倍增做到 \(O(log^2 n)\)。
那么我们怎么找到 \(v\) 呢?考虑把点按 dfs 序排列为 \(p_i\),那么一个点的子树
对应这个排列的一个连续段,所以我们找到最小的 \(v\),使得 \(dp_i \ge sz_1\),
这个 \(v\) 一定在重心的子树内。我们二分查找到这个 \(v\) 即可。
总复杂度 \(O(q log^2n + n)\)。
CF1667E
题面
对于所有点数为 \(n\) 的树,如果其满足对于所有 \(i \in [2, n]\),与\(i\)相连的\(j\)中恰有一个点 \(j\) 满足 \(j < i\) ,那么我们称其为好树。
\(\forall 1 \le i \le n\),求出来有多少好树满足重心为 \(i\)。
重心定义满足为删去该点后形成的所有连通块大小均小于 \(\frac {n−1}{2}\) 的点。
数据范围 \(3 \le n \le 2 \times 105\) 且 \(n\) 为奇数。
思路
设 \(f_i\) 为以 \(i\) 为根的子树大小超过 \(m =\frac{n+1}{2}\)的方案。
那么有:$$f_i=\sum_{j=m}^{n} \binom{n-i}{j-1}(i − 1)(n − j − 1)!(j − 1)! $$
其中 \(\binom{n-i}{j-1} (j-1)!\)为选择 \(j − 1\) 个点挂在 \(i\) 的子树内的方案,\((n − j − 1)!\) 为子树外的点选父亲的方
案,\(i − 1\) 为 $ i$ 选父亲的方案。
考虑设 \(g_i\)为 \(i\) 为重心的方案数。考虑某个点 \(j(j > i)\) 向父亲跳的过程,那么 \(i\)是 \(j\) 的祖先的概率为 \(\frac{1}{i}\)。
那么有:$$g_i=f_i\frac {\sum_{j=i+1}^{n}g_j}{i}$$
复杂度\(O(n)\)
相关题目
CF1707C
[NOIP2013 提高组] 货车运输
CF980E
CF1060E
CF1499F
浙公网安备 33010602011771号