标签:sam 表示 关系 基础 master 基于 循环 种类 时间复杂度
给定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 \rightarrow y\)和\(a \rightarrow b\)有重叠部分,则重叠部分实际没有起到减少路径长度的作用。假设重叠的长度为\(same(x,y,a,b)\),则行走路径总长为\(2n - 2 - (dis(x,y) -1) - (dis(a,b) - 1) + 2 \times same(x,y,a,b) = 2n - dis(x,y) - dis(a,b) + 2 \times same(x,y,a,b)\)
这样1来还怎么搞,\(n^4\)枚举\((x,y,a,b)\)?
怎么搞?想办法搞!
老师,我会大胆猜想!
结论:在\(k = 1\)的基础上做。即:选择的两条路径中,至少有1条是原树的直径。
证明:两条路径的实际贡献,实际为它们不相交部分的总边数,即2个“叉”的总长度。根据直径的最长性,假设我们强制令这两个叉的2个端点为直径的两端点,则答案一定更优。
由此,确定了第1条路径的选取后,第2条路径的选取,只需最大化\(dis(a,b) - 2 \times 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\)条路径总长。
全错辣!不禁骂自己是个sb,混淆了边不能相交与点不能相交的概念。点不能相交意味着1个点至多只能与2个儿子相连,边不能相交就意味着1个点可以至多与4个儿子相连。
这时如果我们还用原本的做法,将状态扩充到\(s = 3\),会觉得组合的种类数过多,转移过于复杂。
考虑维护一些相对简单的状态,用它们组合成可能的结果。
考虑答案的2条路径与当前子树根节点\(x\)的关系。
2条路径由从\(x\)向下的4条直链组成。只需维护\(f(x)(0\rightarrow3)\)表示从\(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)\)。代码见此。
标签:sam 表示 关系 基础 master 基于 循环 种类 时间复杂度
原文地址:https://www.cnblogs.com/littlewyy/p/11742456.html