有点意思又偏向套路的结论题。
我们发现,最终答案一定是类似于一条路径 (mathrm{Path(u o v)}) 上延伸出两条路径 (mathrm{Path(x' o x),Path(y' o y)}) 这种形式,其中 (x',y') 都在路径 (u o v) 上。而我们要先让 (mathrm{dis(x',y')}) 最大(即相交部分最大),其次令路径 (mathrm{Path(u o v),Path(x' o x),Path(y' o y)}) 的长度之和最大。
可以发现 (mathrm{Path(u o v)}) 这条路径,可以使用类似直径的两次 ( ext{dfs}) 求出。而一旦这条路径确定,另外两条路径的 (x',y') 是确定的,对应求出 (x',y') 子树中的叶子节点 (x,y) 即可。答案的链对就为 (mathrm{Path(u o v),Path(x o y)})。
但是如果 (u o v) 不满足一些条件,会导致对应求出的 (x,y) 形成的交集不是最大的,或者路径长度总和不是最大的(我在这个地方反复 ( ext{Wrong Answer on test 72/57/45}),不得不说 ( ext{Codeforces}) 的数据还是挺强的
为了解决这个问题,我们在两遍 ( ext{dfs}) 的时候,按照优先级更新值即可。先考虑相交部分最大,其次考虑路径长度之和最大。由于需要求路径长度之和,我们需要预处理出 (x) 子树内最大距离和次大距离,这是一个套路就不细表了。
那么,我们就可以在 (O(n)) 的时间复杂度内解决这个问题。代码写的有点凌乱随便看看就好(我才不会告诉你是我故意的呢~
#include<cstdio>
int pos=0,val=0,val2=0,cnt=0,mx,mn,rex,rey;
int du[200005],h[200005],to[400005],ver[400005];
int dis[200005],tag[200005],pre[200005],s[200005],leaf[200005],sec[200005];
inline int read() {
register int x=0,f=1;register char s=getchar();
while(s>'9'||s<'0') {if(s=='-') f=-1;s=getchar();}
while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
return x*f;
}
inline void swap(int &x,int &y) {int tmp=y;y=x;x=tmp;}
inline void add(int x,int y) {to[++cnt]=y;ver[cnt]=h[x];h[x]=cnt;}
inline void dfs2(int x) {
du[x]=0;
if(dis[mx]-dis[mn]>val) pos=x,val=dis[mx]-dis[mn],val2=dis[leaf[mn]]+dis[leaf[mx]]+dis[x]; else if(val==dis[mx]-dis[mn]&&dis[leaf[mn]]+dis[leaf[mx]]+dis[x]>val2) pos=x,val2=dis[leaf[mn]]+dis[leaf[mx]]+dis[x];
for(register int i=h[x],y;i;i=ver[i]) {if(pre[x]==(y=to[i])) continue; ++du[x];}
for(register int i=h[x],y;i;i=ver[i]) {
if(pre[x]==(y=to[i])) continue;
pre[y]=x; dis[y]=dis[x]+1; int premx=mx,premn=mn;
int flag=0; if(leaf[y]==leaf[x]) {flag=1;swap(leaf[x],sec[x]);}
if(du[x]>1) {
if(dis[x]>dis[mx]) mx=x;
else if(dis[x]==dis[mx]&&dis[leaf[x]]>dis[leaf[mx]]) mx=x;
if(dis[x]<dis[mn]) mn=x;
else if(dis[x]==dis[mn]&&dis[leaf[x]]>dis[leaf[mn]]) mn=x;
}
dfs2(y); mx=premx; mn=premn;
if(flag) swap(leaf[x],sec[x]);
}
}
inline void dfs3(int x) {
leaf[x]=x;
for(register int i=h[x],y;i;i=ver[i]) {
if(pre[x]==(y=to[i])||tag[y]) continue; pre[y]=x; dfs3(y);
if(dis[leaf[x]]<dis[leaf[y]]) {sec[x]=leaf[x];leaf[x]=leaf[y];}
else if(dis[sec[x]]<dis[leaf[y]]) {sec[x]=leaf[y];}
}
}
int main() {
int n=read(),top=0,st,ed; dis[0]=mx=0; dis[n+1]=mn=n+1;
for(register int i=1;i<n;++i) {int x=read(),y=read();add(x,y);add(y,x);}
dis[1]=pos=val=0; dfs2(1); st=pos; pre[st]=-1; dis[st]=pos=val=0; dfs3(st); mx=0,mn=n+1; dfs2(st); ed=pos;
int cur=ed,rex,rey; while(cur!=-1) {s[++top]=cur; tag[cur]=1; cur=pre[cur];} mx=0,mn=n+1;
for(register int i=top-1;i>=2;--i) {
int x=s[i];
for(register int k=h[x],y;k;k=ver[k]) if(!tag[y=to[k]]) mx=(dis[x]>dis[mx]? x:mx),mn=(dis[x]<dis[mn]? x:mn);
}
dfs3(mn); rex=leaf[mn]; dfs3(mx); rey=leaf[mx];
printf("%d %d
%d %d
",st,ed,rex,rey);
return 0;
}