题目链接: https://ac.nowcoder.com/acm/problem/20532
简化题意
在一棵树上,找到一个点,使得这个点到给定的3个点的距离和最小
分析
问:先考虑简化题目,如果只给定2个点求距离之和最小应该这么做?
答:sb题,直接一个LCA模板就行辣,集合点就在2点之间的最短路径上
问:那么如何求LCA呢
答:倍增求呗,如果被卡常了还可以用树链剖分求LCA
问:具体点,倍增这么求LCA?
答:
那首先要知道倍增是什么
倍增就是 “成倍增加” 的意思,比如 1 倍增后变成了 2,2 倍增后就变成了 4,4 变成 8,以此类推...
考虑暴力求LCA
两个点先到达同一深度,然后一直往上跳父亲,知道两个点跳到同一个点上,这个点就是 LCA。
暴力求复杂度是o(n)的,如果询问多了就凉凉
由于暴力是一个一个跳的,我们可以一次性跳多次
具体可以参见此博客: https://www.cnblogs.com/Kaike/p/12312896.html
问:解决了这些基础知识,可以开始本题了吗?
答:可以了
问:请开始你的表演
答:
首先这个题的点数从两个变成了3个
我们把连接3个点的边单独提取出来
如果3个开始不共点
那么提取出的图中有且只有一个点的度为3
这个点就是我们需要的汇点
考虑这个点的性质,我们可以发现这个点是属于3个点两两组合后的LCA
但是3个点两两组合后的LCA会有多个点
我们发现这个汇点是深度最深的LCA点
判断一下,我们就找到了汇点
接下来求距离就简单了
此题完
参考代码
#include<bits/stdc++.h> #define N 500005 using namespace std; int n,m; struct oppo{ int to,nex; }rod[N*2]; int head[N],tot; void add(int from,int to) { rod[++tot].to=to; rod[tot].nex=head[from]; head[from]=tot; } int dep[N]; int fa[N][22]; void dfs(int x,int father) { fa[x][0]=father; dep[x]=dep[father]+1; for(int i=head[x];i;i=rod[i].nex){ int to=rod[i].to; if(to==father) continue; dfs(to,x); } } int lca(int x,int y) { if(dep[x]<dep[y]) swap(x,y); for(int i=20;i>=0;i--) if(dep[fa[x][i]]>=dep[y]) x=fa[x][i]; if(x==y) return x; for(int i=20;i>=0;i--) if(fa[x][i]!=fa[y][i]){ x=fa[x][i]; y=fa[y][i]; } return fa[x][0]; } void work() { dep[0]=-1; dfs(1,0); for(int k=1;k<=20;k++) for(int i=1;i<=n;i++) fa[i][k]=fa[ fa[i][k-1] ][k-1]; } int dis(int x,int y) { return dep[x]+dep[y]-2*dep[lca(x,y)]; } int main() { cin>>n>>m; for(int i=1;i<n;i++){ int s,t; scanf("%d%d",&s,&t); add(s,t); add(t,s); } work(); while(m--){ int a,b,c; scanf("%d%d%d",&a,&b,&c); int LCA=0; int l1=lca(a,b),l2=lca(b,c),l3=lca(a,c); if(dep[l1]>dep[LCA]) LCA=l1; if(dep[l2]>dep[LCA]) LCA=l2; if(dep[l3]>dep[LCA]) LCA=l3; if(LCA==l1) printf("%d %d\n",LCA,dep[a]+dep[b]-2*dep[LCA]+dis(LCA,c)); else if(LCA==l2) printf("%d %d\n",LCA,dep[b]+dep[c]-2*dep[LCA]+dis(LCA,a)); else if(LCA==l3) printf("%d %d\n",LCA,dep[a]+dep[c]-2*dep[LCA]+dis(LCA,b)); } return 0; }