题目链接: 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;
}
京公网安备 11010502036488号