题目描述
输入描述
输出描述
输入样例
2
3 2
1 2 10
3 1 15
1 2
2 3
2 2
1 2 100
1 2
2 1
输出样例
10
25
100
100
典型的 LCA 模板题,实质就是求出两点间的最近公共祖先,若最近公共祖先为两点其中一点,则最短距离为另一结点到最近公共祖先的距离;反之则为两点到最近公共祖先距离之和。
本题的朴素做法是将两个结点跳到同一深度后,每次同时向上跳一个深度进行比对,相同则说明该结点即为最近公共祖先,否则继续往上跳直到符合要求为止。但由于该做法的时间复杂度男默女泪,因此可考虑类似于二分原理的倍增。
由于最近公共祖先的所有祖先结点都是所求两点的公共祖先,因此可判断两点倍增后的结点是否相同(此处倍增从更靠近根节点的祖先结点开始,直到父结点)。若相同,则说明最近公共祖先即为该点或深度更深的点;若不同,则更新该结点为其祖先结点,并重复以上步骤。
因此,本题需要用 dfs 预处理出每个结点的深度从而得到每个结点对应的倍增后的祖先,以及初始两结点间的深度差(需要将两结点处理至同一深度)。
核心代码【尚未完成】
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
vector<int> g[N];
int father[N][40]={0};
int depth[N]={0};
int n,m;
bool visit[N]={false};
int root;
void dfs(int u){
int i;
visit[u]=true;
for(int i=0;i<g[u].size();i++){
int v=g[u][i];
if(!visit[v]){
depth[v]=depth[u]+1;
dfs(v);
}
}
}
void bz(){
int i,j;
for(j=1;j<=30;j++)
for(i=1;i<=n;i++)
father[i][j]=father[father[i][j-1]][j-1];
}
int LCA(int u,int v){
if(depth[u]<depth[v])
swap(u,v);
int dc=depth[u]-depth[v];
int i;
for(int i=0;i<30;i++)
if((1<<i)&dc)
u=father[u][i];
if(u==v)
return u;
for(i=29;i>=0;i--)
if(father[u][i]!=father[v][i]){
u=father[u][i];
v=father[v][i];
}
u=father[u][0];
return u;
}