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

题目链接

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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值