[树的直径]F. Three Paths on a Tree

F. Three Paths on a Tree

Description

You are given an unweighted tree with nn vertices. Recall that a tree is a connected undirected graph without cycles.

Your task is to choose three distinct vertices a,b,ca,b,c on this tree such that the number of edges which belong to at least one of the simple paths between aa and bb, bb and cc, or aa and ccis the maximum possible. See the notes section for a better understanding.

The simple path is the path that visits each vertex at most once.

Input

The first line contains one integer number nn (3≤n≤2⋅1053≤n≤2⋅105) — the number of vertices in the tree.

Next n−1n−1 lines describe the edges of the tree in form ai,biai,bi (1≤ai1≤ai, bi≤nbi≤n, ai≠biai≠bi). It is guaranteed that given graph is a tree.

output

In the first line print one integer resres — the maximum number of edges which belong to at least one of the simple paths between aa and bb, bb and cc, or aa and cc.

In the second line print three integers a,b,ca,b,c such that 1≤a,b,c≤n1≤a,b,c≤n and a≠,b≠c,a≠ca≠,b≠c,a≠c.

If there are several answers, you can print any.

Examples

Input

8
1 2
2 3
3 4
4 5
4 6
3 7
3 8

Output

5
1 8 6

Note

The picture corresponding to the first example (and another one correct answer):

If you choose vertices 1,5,61,5,6 then the path between 11 and 55 consists of edges (1,2),(2,3),(3,4),(4,5)(1,2),(2,3),(3,4),(4,5), the path between 11 and 66 consists of edges (1,2),(2,3),(3,4),(4,6)(1,2),(2,3),(3,4),(4,6) and the path between 55 and 66 consists of edges (4,5),(4,6)(4,5),(4,6). The union of these paths is (1,2),(2,3),(3,4),(4,5),(4,6)(1,2),(2,3),(3,4),(4,5),(4,6) so the answer is 55. It can be shown that there is no better answer.

正确解法:

在树上选出三个点,使他们的边的并集最大。

这引入了树的直径,树的直径的边最长。假设a、b是树的两个直径端点,c是除了a、b的任意一个点

ans=(dis(a,c)+dis(a,b)+dis(b,c))/2;

两次BFS求树的直径与端点:

那么问题来了,怎么求树的直径的呢?这里提供一种两次BFS求树的直径的方法:

先任选一个起点BFS找到最长路的终点,再从终点进行BFS,则第二次BFS找到的最长路即为树的直径;
 原理: 设起点为u,第一次BFS找到的终点v一定是树的直径的一个端点
 证明: 1) 如果u 是直径上的点,则v显然是直径的终点(因为如果v不是的话,则必定存在另一个点w使得u到w的距离更长,则于BFS找到了v矛盾)
     2) 如果u不是直径上的点,则u到v必然于树的直径相交(反证),那么交点到v 必然就是直径的后半段了
 所以v一定是直径的一个端点,所以从v进行BFS得到的一定是直径长度

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cmath>
 4 #include <algorithm>
 5 #include <set>
 6 #include <queue>
 7 #include <stack>
 8 #include <string>
 9 #include <cstring>
10 #include <vector>
11 #include <map>
12 //#include <unordered_map>
13 #define mem( a ,x ) memset( a , x ,sizeof(a) )
14 #define rep( i ,x ,y ) for( int i = x ; i<=y ;i++ )
15 #define lson  l ,mid ,pos<<1
16 #define rson mid+1 ,r ,pos<<1|1
17 using namespace std;
18 typedef long long ll ;
19 typedef pair<int ,int> pii;
20 typedef pair<ll ,int> pli;
21 const int inf = 0x3f3f3f3f;
22 const ll mod=998244353;
23 const int N=1e5+50;
24 int n,xx,yy,pos;
25 int Link[2*N],len=0,bok[2*N],dis[2*N];
26 int dis1[2*N],dis2[2*N];
27 int que[2*N];
28 struct node
29 {
30     int y,next;
31 }e[4*N];
32 void insert(int xx,int yy)
33 {
34     e[++len].next=Link[xx];
35     Link[xx]=len;
36     e[len].y=yy;
37 }
38 void dfs(int x)
39 {
40     for(int i=1;i<=n;i++)
41         bok[i]=0,dis[i]=inf;
42     bok[x]=1,dis[x]=0;
43     int head=1,tail=2;
44     que[1]=x;
45     pos=x;
46     while(head<tail)
47     {
48         int u=que[head];
49         for(int i=Link[u];i;i=e[i].next)
50         {
51             int v=e[i].y;
52             if(bok[v])  continue;
53             bok[v]=1;
54             if(dis[v]>dis[u]+1)
55             {
56                 dis[v]=dis[u]+1;
57                 que[tail++]=v;
58             }
59             if(dis[v]!=inf&&dis[v]>dis[pos])
60                 pos=v;
61         }
62         head++;
63     }
64 }
65 int main()
66 {
67     scanf("%d",&n);
68     for(int i=1;i<n;i++)
69     {
70         scanf("%d%d",&xx,&yy);
71         insert(xx,yy);
72         insert(yy,xx);
73     }
74     int a,b,c=0;
75     dfs(1),a=pos;
76     dfs(pos),b=pos;
77     for(int i=1;i<=n;i++)   dis1[i]=dis[i];
78     //cout<<a<<endl;
79     dfs(pos);
80     for(int i=1;i<=n;i++)   dis2[i]=dis[i];
81     //cout<<b<<endl;
82     int ans=0;
83     for(int i=1;i<=n;i++)
84     {
85         if(i==a||i==b)  continue;
86         if(ans<dis1[i]+dis2[i])
87         {
88             ans=dis1[i]+dis2[i];
89             c=i;
90         }
91     }
92     //cout<<c<<endl;
93     printf("%d\n",(ans+dis1[b])/2);
94     printf("%d %d %d\n",a,b,c);
95
96     return 0;
97 }

------------------------------------------------------------------------------------------------------

树形DP

对于每个节点我们要记录两个值:

f1 [ i ] 表示以 i 为根的子树中,i 到叶子结点距离的最大值

f2 [ i ] 表示以 i 为根的子树中,i 到叶子结点距离的次大值

对于一个节点,它到叶子结点距离的最大值和次大致所经过的路径肯定是不一样的

若j是i的儿子,那么(下面的 w [ i ][ j ] 表示 i 到 j 的路径长度):

若 f1 [ i ] < f1 [ j ] + w [ i ][ j ],f2 [ i ] = f1 [ i ],f1 [ i ] = f1 [ j ] + w [ i ][ j ];
否则,若 f2 [ i ] < f1 [ j ] + w [ i ][ j ],f2 [ i ] = f1 [ j ] + w [ i ][ j ]; 
理解:这样做就是,先看能否更新最大值,若能,它的次大值就是原先的最大值,再更新它的最大值;若不能,就看能不能更新次大值,若能,就更新,不能就不管它

这样的话,最后的答案 answer = max { f1 [ i ] + f2 [ i ] }

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int N=100005;
 6 int n,m,t,ans;
 7 int f1[N],f2[N];
 8 int first[N],v[N],w[N],next[N];
 9 void add(int x,int y,int z)
10 {
11     t++;
12     next[t]=first[x];
13     first[x]=t;
14     v[t]=y;
15     w[t]=z;
16 }
17 void dp(int x,int father)
18 {
19     int i,j;
20     for(i=first[x];i;i=next[i])
21     {
22         j=v[i];
23         if(j==father)
24           continue;
25         dp(j,x);
26         if(f1[x]<f1[j]+w[i])
27         {
28             f2[x]=f1[x];
29             f1[x]=f1[j]+w[i];
30         }
31         else if(f2[x]<f1[j]+w[i])
32           f2[x]=f1[j]+w[i];
33         ans=max(ans,f1[x]+f2[x]);
34     }
35 }
36 int main()
37 {
38     int x,y,z,i;
39     scanf("%d%d",&n,&m);
40     for(i=1;i<=m;++i)
41     {
42         scanf("%d%d%d",&x,&y,&z);
43         add(x,y,z);
44         add(y,x,z);
45     }
46     dp(1,0);
47     printf("%d",ans);
48     return 0;
49 }

原文地址:https://www.cnblogs.com/Kaike/p/12302102.html

时间: 2024-08-29 16:02:22

[树的直径]F. Three Paths on a Tree的相关文章

树的直径| CF#615Div3 F. Three Paths on a Tree

F. Three Paths on a Tree 思路 两种方法: 1.两次bfs求树的直径,顺便求出一个直径端点到所有点的最短距离:再bfs一次,求另一个直径上的端点到其它所有点的最短距离:之后枚举第三个端点(不等于端点1和端点2),dis(a,b) + dis(b,c) + dis(a,c) 再除以 2 就是最终答案,因为每个路径走了两次所以除以2. 2.dfs求树的直径,记录直径上的所有点.从直径上的所有点去搜索它们到不在直径上的点的最远距离.最后直径+这个最远距离就是答案 代码1 bfs

Codeforces Round #615 (Div. 3) F. Three Paths on a Tree

F. Three Paths on a Tree 原题链接:https://codeforces.com/contest/1294/problem/F 题目大意: 给定一棵树,选出三点,使三点连成的j简单路径最大.简而言之,三个点连成的边的集合大小. 解题思路: 假设任取一点为三点连线的公共点,最长路径就是这个点到其他三个点的三条最长边之和,可知这个点一定在直径上(画图分析假设不在时的最长路径可反证).所以先求出树的直径,在使用$ans =(a b+a c+b c) / 2$遍历可以得到第三个点

树的直径求法与性质(附例题)

树的直径指树上距离最远的两点间的距离,它在树上问题上有许多应用,往往通过树的直径的性质可以将一个高时间复杂度的解法变为线性求解.对于树上两点间距离通常有三种定义,我们根据这三种情况分别讨论一下它的性质 树的直径的求法: 树的直径有两种求法,时间复杂度都是O(n) ①贪心求法: 贪心求直径的方法是任意找一个点为根,dfs整棵树找到距离他最远的点xx,再以这个点x为根求出距离它最远的点y,(x,y)即为直径 ②DP求法: DP求直径的方法是对于每个点记录这个点子树中的最长链及与最长链处于不同子树中的

Codeforces 14D Two Paths 树的直径

题目链接:点击打开链接 题意:给定一棵树 找2条点不重复的路径,使得两路径的长度乘积最大 思路: 1.为了保证点不重复,在图中删去一条边,枚举这条删边 2.这样得到了2个树,在各自的树中找最长链,即树的直径,然后相乘即可 #include<stdio.h> #include<iostream> #include<string.h> #include<set> #include<vector> #include<map> #includ

F - Warm up - hdu 4612(缩点+求树的直径)

题意:有一个无向连通图,现在问添加一条边后最少还有几个桥 分析:先把图缩点,然后重构图为一棵树,求出来树的直径即可,不过注意会有重边,构树的时候注意一下 *********************************************************************** #pragma comment(linker, "/STACK:102400000,102400000")#include<stdio.h>#include<string.h

CodeForces 14D 树的直径 Two Paths

给出一棵树,找出两条不相交即没有公共点的路径,使得两个路径的长度的乘积最大. 思路:枚举树中的边,将该边去掉,分成两棵树,分别求出这两棵树的直径,乘起来维护一个最大值即可. 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 using namespace std; 6 7 const int maxn = 200 + 10; 8 9

树的直径 Codeforces Beta Round #14 (Div. 2) D. Two Paths

tiyi:给你n个节点和n-1条边(无环),求在这个图中找到 两条路径,两路径不相交,求能找的两条路径的长度的乘积最大值: #include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <queue> #include <stack>

Codeforces1294F. Three Paths on a Tree(两次BFS求树的直径)

题意: 给一棵树,找到三个顶点,使三个顶点两两之间路径的并集最大 思路: 必定会有一组最优解,使得 a,b是树直径上的端点. 证明: 假设某个答案取连接点x.x最远的树到达的点是s,根据树的直径算法,s是树的某个直径a的端点.假设x的最远和第二远的点组成的链是b,b就会和a有一段公共部分.我们取a和b相交部分距离s最远的那个点y.那么取这个链上点y的答案一定比x更优 用两次BFS可以求出直径的两个端点,在这个过程中还能顺便求出一个端点到树上每一点的距离.之后再用一次BFS求得另一个端点到树上每一

poj 1985 Cow Marathon 【树的直径】

题目:poj 1985 Cow Marathon 题意:给出一个树,让你求树的直径. 分析: 树的直径:树上两点之间的最大距离. 我们从任意一点出发,BFS一个最远距离,然后从这个点出发,在BFS一个最远距离,就是树的直径. AC代码: /* POJ:1985 Cow Marathon 2014/10/12/21:18 Yougth*/ #include <cstdio> #include <iostream> #include <algorithm> #include