F. Three Paths on a Tree
题意
给一个树,让在树上找三个结点A,B,C,使得ABC三个结点3条路径中出现的结点数最多
分析
因为这是一棵树,三条路径必定会有一个交点O,所以我们需要让OA+OB+OC的长度尽可能的长,而OA+OB我们可以直接认为是树的直径,OC认为是这条直径上最长的一个分支。
具体做法
这里我用的是两次DFS求树的直径以及经过的路径结点,
第一次DFS:先从任意一个结点开始DFS到最远的距离,保存最远点A
第二次DFS:从A再进行一次DFS到最远的距离B,并且记录B->A的路径(倒着存的)
此时就以及求出了直径了,也就是OA+OB
第一次遍历直径中的结点,在vis[]数组上做访问标记
第二次遍历直接中的结点,对每一个结点进行DFS寻找最远的分支C点(访问过的不再访问)
最后答案ABC都求出来了
DFS遍历次数比较多,但是总共遍历树只会遍历3次 也就是O(3*n)
代码
#include <iostream> #include <algorithm> #include <vector> #include <cstring> using namespace std; const int maxn = 1e6+10; int N,A,B,C,mx; int h[maxn],e[maxn],ne[maxn],idx; int last[maxn],vis[maxn]; void add(int a,int b){ e[idx] = b,ne[idx] = h[a],h[a] = idx++; } void DFS(int u,int fa = -1,int dis = 0){ if(dis>=mx){ A = u; mx = dis; } for(int i = h[u];i!=-1;i = ne[i]){ int v = e[i]; if(v == fa) continue; DFS(v,u,dis+1); } } void DFS2(int u,int fa = -1,int dis = 0){ last[u] = fa; if(dis>=mx) { B = u,mx = dis; } for(int i = h[u];i!=-1;i = ne[i]){ int v = e[i]; if(v == fa) continue; DFS2(v,u,dis+1); } } void DFS3(int u,int fa = -1,int dis = 0){ if(dis>=mx){ C = u,mx = dis; } for(int i = h[u];i!=-1;i = ne[i]){ int v = e[i]; if(v == fa || vis[v]) continue; DFS3(v,u,dis+1); } } int main(){ cin>>N; int a,b; memset(h,-1,sizeof(h)); for(int i = 0;i<N-1;i++){ scanf("%d%d",&a,&b); add(a,b); add(b,a); } mx = 0;DFS(1); mx = 0;DFS2(A); vis[B] = 1;vis[A] = 1; for(int i = last[B];i!=A;i = last[i]) { vis[i] = 1; } int d = mx;mx = 0; for(int i = last[B];i!=A;i = last[i]) DFS3(i); cout<<d+mx<<endl; cout<<A<<" "<<B<<" "<<C<<endl; return 0; }