luogu 3629
Posted littlewyy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了luogu 3629相关的知识,希望对你有一定的参考价值。
原题链接
题意
给定1棵(n)个点的树,各边边权均为1。你要从1号点出发,经过所有的点最后返回1号点。
现在你要在这棵树上新增恰好(k)条边,且必须保证你的行走路线经过这(k)条边恰好1次。
请你为这(k)条边指定端点,使得行走路径长度最小化。(3 leq n leq 10^5 , 1 leq k leq 2)。
题解
(k = 0)时路径长度只能为(2n - 2)。
(k = 1)时,若新边为((x,y)),则意义在于从1到达(x)之后,不用返回1,直接到达(y),(y)直接到1。因此节省的路径长度为(dis(x,y) - 1)。故求1次树的直径即可。
(k = 2)时,若第2条新边为((a,b)),假若(x ightarrow y)和(a ightarrow b)有重叠部分,则重叠部分实际没有起到减少路径长度的作用。假设重叠的长度为(same(x,y,a,b)),则行走路径总长为(2n - 2 - (dis(x,y) -1) - (dis(a,b) - 1) + 2 imes same(x,y,a,b) = 2n - dis(x,y) - dis(a,b) + 2 imes same(x,y,a,b))
这样1来还怎么搞,(n^4)枚举((x,y,a,b))?
怎么搞?想办法搞!
解法一
老师,我会大胆猜想!
结论:在(k = 1)的基础上做。即:选择的两条路径中,至少有1条是原树的直径。
证明:两条路径的实际贡献,实际为它们不相交部分的总边数,即2个“叉”的总长度。根据直径的最长性,假设我们强制令这两个叉的2个端点为直径的两端点,则答案一定更优。
由此,确定了第1条路径的选取后,第2条路径的选取,只需最大化(dis(a,b) - 2 imes same(x,y,a,b))
看起来选择((a,b))还要考虑其与((x,y))的公共路径长度,很不好处理。但本质上这个问题仍旧是树上最优化路径问题,考虑将其转化成求树直径这一能够(O(n))求解的经典问提。具体地,我们可以将所谓公共部分的影响,转化成静态的贡献,即将((x,y))上的边权置为(-1),即可得解。
顺带一提,当树上存在负权边时,不能够通过两次BFS/DFS求直径。原因是两次BFS/DFS求直径的正确性基于贪心思想,即类似a > b则a + c > b的式子,与dijkstra是一个道理。通用的求解直径的方法是树形Dp。这一点也启发我们,学算法要学到本质上,不能似是而非。
时间复杂度(O(n))。代码见此
解法二
老师,我会转换思路,看透本质!
上文的做法是基于先考虑各自的贡献,再减去重复部分的贡献。
还有1种不错的思路是,只考虑非重复部分的贡献。
具体地,对于两条相交路径,我们只对不相交部分计数。可以发现不相交部分一定可以组成2条不相交路径。因此问题转化为,在树上选择(2)条不相交路径,最大化(2)条路径总长。
点击查看初始思路
考虑树形Dp,对于根节点x,我们对各棵子树依次考虑。对于当前子树y,有以下几种情况: 1. x与y相连,且y的子树内,除了y直下的路径,还存在1条与其不相交的路径 2. x与y相连,且y的子树内仅有1条从y直下的路径 3. x与y不相连,且y连接情况任意,y的子树内有任意条不相交路径 据此,设计状态f(i)(j)(s)表示以i为根的子树中,共有j条不相交路径,且i的状态为s的最大路径长度和。s = 0表示i没有连边,s = 1表示i从1个儿子直下,s = 2表示i从2个儿子直下。 状态转移方程: 1. s = 0,枚举j和lj,令f(i)(lj)(0)与f(y)(j - lj)(0/1/2)进行组合 2. s = 1,i从y直下 (1) j = 1 f(i)(1)(1) = max(f(i)(1)(1),f(y)(1)(1) + 1) (2)j = 2 1条路径来自之前的子树,f(i)(2)(1) = max(f(i)(2)(1),f(i)(1)(0) + f(y)(1)(1) + 1) 2条路径均来自该子树,f(i)(2)(1) = max(f(i)(2)(1),f(y)(2)(1) + 1) 3. s = 2,i的其中1条从之前儿子直下,1条从y直下 (1)j = 1 f(i)(1)(2) = max(f(i)(1)(2),f(i)(1)(1) + f(y)(1)(1) + 1) (2)j = 2 前2条后1条,f(i)(2)(2) = max(f(i)(2)(2),f(i)(2)(1) + f(y)(1)(1) + 1) 前1条后2条,f(i)(2)(2) = max(f(i)(2)(2),f(i)(1)(1) + f(y)(2)(1) + 1) 值得注意的是,s较大的情况,有赖于s较小且不包含该子树的dp值,因而应该倒序循环全错辣!不禁骂自己是个sb,混淆了边不能相交与点不能相交的概念。点不能相交意味着1个点至多只能与2个儿子相连,边不能相交就意味着1个点可以至多与4个儿子相连。
这时如果我们还用原本的做法,将状态扩充到(s = 3),会觉得组合的种类数过多,转移过于复杂。
考虑维护一些相对简单的状态,用它们组合成可能的结果。
考虑答案的2条路径与当前子树根节点(x)的关系。
2条路径由从(x)向下的4条直链组成。只需维护(f(x)(0 ightarrow3))表示从(x)向下前4长的直链长度,用(sum _{i = 0}^{i = 3}f(x)(i))更新答案。
2条路径都不经过(x)。
要么这2条路径集中于同1个子树中,这种情况与(x)毫无关系,会在递归到子树(y)时求解。
要么这2条路径分散于2个子树中,故维护(d(x))表示以(x)为根的子树的直径,用(d(y_1) + d(y_2))更新答案,其中(y_1,y_2)是(x)的不同的2个儿子。
1条路径经过(x),另1条在某子树(y)中。对于当前访问的儿子(y),
要么(x)与(y)不相连,且(y)贡献1条路径,则用(f(x)(0) + f(x)(1) + d(y))更新答案。
要么(x)与(y)相连,(y)除了贡献路径的半边,还贡献多1条不相交路径。考虑(g(x))表示在以(y)为根的子树中,除了从(x)节点直下,还有1条与之不相交路径的最长总长度;(g(x))在访问子树(y)时可用(f(x)(0) + d(y))、(max(d(prey)) + f(y)(0) + 1)、(g(y) + 1)、(f(x)(0) + f(x)(1) + f(x)(2))进行维护。则用(f(x)(0) + g(y) + 1)更新答案。
要么(x)与(y)相连,(y)仅贡献路径的半边。则用(g(x) + f(y)(0) + 1)贡献答案。
值得注意的是,语句的顺序十分重要,当需要用到之前子树的信息时,不可以用已被(y)更新过的信息!
时间复杂度(O(n))。代码见此。
以上是关于luogu 3629的主要内容,如果未能解决你的问题,请参考以下文章