2025 刷题计划 - 树上问题
2025 刷题计划 - 树上问题
A. CF609E Minimum spanning tree for each edge
几个月前也许还觉得有点难,现在已然成为傻逼题。
B. CF70E Information Reform
树形 DP 好题。一开始想成了换根,想了 2h 发现不太可做,主要是不会设计状态。套路地将节点 \(u\) 选还是不选设入状态这种方法是不可做的。
观察到 \(n\le180\),在树上问题中这个数据范围不多见,大抵是一个 \(O(n^3)\) 的算法,那么首先不管用 Floyd 还是 DFS 求出两点间距离记作 \(\text{dis}(u,v)\) 是容易的。
那么状态设计就是本题的难点,首先得到一个极为重要的引理如下。这个引理告诉我们,对于一个节点 \(u\) 所辐射的点形成一个连通块。那么既然形成了一个连通块了,就可以将问题转化为:把树分为若干个连通块,每个连通块选择一个区域信息中心的代价。
引理:设 \(t_i\) 是离节点 \(i\) 最近的区域信息中心。对于树上两点 \(u,v\),若 \(t_u=t_v=k\),则在 \(u\to v\) 路径上的所有点 \(w\) 都满足 \(t_w=k\)。
那么这个问题就简单了,对于每个连通块在其根部计算贡献,设 \(f_{u,j}\) 为以 \(u\) 为根的子树中 \(t_u=j\) 的最小代价,则有
实际实现中,我们只需要再记录一个使 \(f_{u,j}\) 最小的 \(j\),记作 \(g_u\),就可以把上述转移方程优化到 \(O(n^3)\)。
输出方案也是容易的。
C. P7359 「JZOI-1」旅行
暴力 DP 是容易的,复杂度 \(O(n^2)\)。想要优化,因为是无根树,我们就得开两个 DP 数组,一个记录他到他爸,一个记录他爸到他的答案。
我们现在的诉求是快速求出树上一段路径的 DP 值。可以尝试倍增或树剖套广义矩阵乘法。广义矩阵乘法应该是动态 DP 里面要用的,所以这题也算半个动态 DP。
广义矩阵乘法公式为:
\[C_{i,j}=\min\{A_{i,k}+B_{k,j}\} \]实际上就是把原 \(\sum\) 改为 \(\min\)、原 \(\times\) 改为 \(+\)。
考虑如何构建矩阵。写出转移方程:
所以矩阵就长这样:
手模一下。然后倍增就正常预处理,注意乘法顺序。
D. P9399 「DBOI」Round 1 人生如树
纯纯诈骗题。就是一个简化版的汽油价格。
你会发现我们只要搞出来两个数组的哈希,如果合法,它们的哈希值之差就应该是类似 \(\dots54321\) 这种(看作 \(P\) 进制,其中 \(P\) 是哈希底数)。然后你发现这个东西纯纯可以预处理。又因为有单调性直接二分答案。
剩下的不就全是那道题的套路了么,什么树状数组、树剖给它一搞就完了。
什么?你担心怎么修改?修改根本不影响原来的答案!所以把询问离线,全修改完之后再做。反正这题又不强制在线。
诈骗题。根本不用什么差分。做题要懂得想以前的套路。
E. CF1023F Mobile Phone Network
和 A 一样的傻逼题。一个数组开小导致的 WA 调了 1h,最后还是 CF 的 AI 告诉我的。
F. CF293E Close Vertices
点分治维护树上二维偏序的板题。套路依然是一维用排序解决,另一维用树状数组解决,保证树状数组里的值都满足第一维偏序的前提下维护第二维偏序。
但这样的话同一子树内的点会算重,所以需要容斥一下。我的第一种写法在 CF 上评测 WA#6,但本地半个小时拍不出错误。换一种写法一遍过。能过的写法是在 calc
函数中容斥,每遍历一个儿子容斥掉当前子树,最后把所有子树的答案搂一块加上。divide
函数永远只负责分治,不负责统计答案;dis
数组里不能含有 0,而是最后把树状数组里剩余的再加上。
G. CF1521D Nastia Plays with a Tree
傻逼贪心。套路依然是先断边再连边。为了使总断边次数最小,优先断掉与父亲的边(因为这样就能少断一个儿子)。
树上贪心一定自下而上,默认处理完了所有子树后再考虑当前节点。
树上贪心一定自下而上,默认处理完了所有子树后再考虑当前节点。
树上贪心一定自下而上,默认处理完了所有子树后再考虑当前节点。
H. AT_arc165_e [ARC165E] Random Isolation
看起来很典的 DP 题,但并不简单。
由于期望的线性性,套路地把期望拆到每个点上。设 \(E(x)\) 表示点 \(x\) 产生贡献的概率,则答案显然就是 \(\sum E(x)\)。至于求这个 \(E(x)\),把操作转化为:对于随机的长度为 \(n\) 的排列 \(p\),按照 \(p_1,p_2,\dots,p_n\) 的顺序依次考虑 \(p_i\) 所在连通块的大小,根据题目所给的方法进行操作。易得这样转化的做法正确,因为无效点一旦删去不会对之后答案产生影响。
那么,点 \(x\) 产生贡献当且仅当它所在的连通块大小 \(>k\),现在问题变为,统计每个这样的连通块出现的概率。
设连通块大小为 \(x\),与该连通块相连(不属于该连通块)的点的个数为 \(y\),放到排列 \(p\) 上就是 \(\boldsymbol x\) 个节点在 \(\boldsymbol y\) 个节点之后的概率,易得这个概率为 \(\dfrac{x!y!}{(x+y)!}\)。
那么现在只剩下最后一个任务就是统计每一种 \((x,y)\) 的连通块有多少个,到这一步就可以树上背包解决了。总复杂度 \(O(n^2k^2)\)。
后面两道一道原,一道傻逼莫队,没了。