F. Three Paths on a Tree
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output
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 cc is 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.
Example
input
Copy
8
1 2
2 3
3 4
4 5
4 6
3 7
3 8
output
Copy
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.
题意:
给你一颗无向无权树,结点个数n<=2e5,让你选三个点a,b,c
使得在a到b的路径上或者在a到c的路径上或者在b到c的路径上的边数最大,输出最大边数和你选的三个点,多个答案输出任意一个。
思路:
由于树中任意两个点之间的路径唯一,我们不难发现在答案a,b,c中,一定有两个点是树的直径中的两个端点(有兴趣的可以自己证明一下)。因此我们用dfs求树的任意一条直径,并标记所求直径上的点,最后从标记的点搜未标记的点的最远距离,加上树的直径就是答案。注意a,b,c中不能有相同的点。
不能从未标记的点开始搜到标记的点。否则会影响其他未标记的点到标记点的长度。
复杂度O(n)
也可以用LCA做。有兴趣者可以自己查找一下LCA的做法。
代码:
#include<iostream>
#include<stdio.h>
#include<map>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<vector>
#include<queue>
#define inf 0x3f3f3f3f
using namespace std;
const int N = 4e5+10;
int n,m,maxid,maxn;
vector<int> G[N];
int f[N];int d[N];
int vis[N];int path[N];
int book[N];int du[N];
int ans,ct,nid;
void dfs(int x){
for(int i=0;i<G[x].size();i++){
int u=G[x][i];
if(vis[u]==1) continue;
vis[u]=1;
if(d[u]<d[x]+1){
d[u]=d[x]+1;
if(maxn<d[u]){
maxid=u;
maxn=d[u];
}
path[u]=x;
}
dfs(u);
}
return;
}
void dfss(int x,int num)
{
//if(num==2) cout<<"& "<<x<<endl;
if(ct<num){ct=num;nid=x;}
for(int i=0;i<G[x].size();i++){
int u=G[x][i];
if(book[u]==1) continue;
book[u]=1;
dfss(u,num+1);
}
return;
}
int main(){
int cnt=1,st,ed,idd;
scanf("%d",&n);
{
m=n-1;
int flag=0;
for(int i=1;i<=n;i++) G[i].clear();
for(int i=1;i<=n;i++) book[i]=0;
for(int i=1;i<=m;i++){
int ta,tb;
scanf("%d%d",&ta,&tb);
G[ta].push_back(tb);
G[tb].push_back(ta);
}
for(int i=1;i<=n;i++) vis[i]=0;
maxn=0;
maxid=0;
dfs(1);
int tem=maxid;
for(int i=1;i<=n;i++) vis[i]=0;
for(int i=1;i<=n;i++) path[i]=-1;
for(int i=1;i<=n;i++) d[i]=0;
maxid=0;
maxn=0;
path[tem]=-1;
vis[tem]=1;
dfs(tem);
st=maxid;
int idd;
for(int i=maxid;i!=-1;i=path[i]){
book[i]=1;
ed=i;
}
for(int i=1;i<=n;i++)
if(book[i]&&i!=st&&i!=ed) idd=i;
ct=0,nid=idd;
ans=maxn;
for(int i=1;i<=n;i++)
if(book[i]==1){//注意是从标记的点开始搜未标记的点
ct=0;
dfss(i,0);
if(ans<maxn+ct)
{
//cout<<"debug: "<<i<<" "<<maxn<<" * "<<ct<<endl;
ans=maxn+ct;
idd=nid;
}
}
printf("%d\n%d %d %d\n",ans,st,ed,idd);
}
return 0;
}