Description
给定一棵无根树,边权都是1,请去掉一条边并加上一条新边,定义直径为最远的两个点的距离,请输出所有可能的新树的直径的最小值和最大值。
Input
第一行包含一个正整数n(3<=n<=500000),表示这棵树的点数。
接下来n-1行,每行包含两个正整数u,v(1<=u,v<=n),表示u与v之间有一条边。
Output
第一行输出五个正整数k,x1,y1,x2,y2,其中k表示新树直径的最小值,x1,y1表示这种情况下要去掉的边的两端点,x2,y2表示这种情况下要加上的边的两端点。
第二行输出五个正整数k,x1,y1,x2,y2,其中k表示新树直径的最大值,x1,y1表示这种情况下要去掉的边的两端点,x2,y2表示这种情况下要加上的边的两端点。
若有多组最优解,输出任意一组。
Sample Input
6
1 2
2 3
2 4
4 5
6 5
Sample Output
3 4 2 2 5
5 2 1 1 6
Solution:
一道树的直径裸题。
首先要抛开打印解的部分,因为求得应该断哪个点之后,我们可以直接在两个连通块内找到直径端点或者最靠近直径中点的点,时间复杂度是 O(n) 的。结果我一开始并没有这么考虑,反而想着一边找最优解一边求方案,结果复杂度就无故增高,还增加了代码难度。
显然这个修改的边一定和这棵树的原直径,于是先将将树的直径抽离出来。接下来分别讨论两种最值的情况。
对于最小值,显然我们应该断在直径上。因为断在其他边不会改变直径的长度,而我们的目的又是让直径尽可能小。接下来对于断出的两个连通块,最后得到的连通块最小直径取决于以下三种情况:
- 在连通块A内。
- 在连通块B内。
- 同时经过连通块A、B。
对于第三种情况,肯定是每个连通块最靠近中点的点——我们定义中点指的是这棵树直径的半径点位置,显然这个中点不一定在顶点上,也可能在边上,那么这个最靠近中点的点有这样一个性质:
- 树的直径两端点到该点的最大值最小。
这个结论在IOI2013_dreaming也有体现。那么新的直径的最小值即为: