倍增算法可以在线求树上两个点的LCA,时间复杂度为nlogn
预处理:通过dfs遍历,记录每个节点到根节点的距离dis[u],深度dep[u]
i的2^j祖先就是i的(2^(j-1))祖先的2^(j-1)祖先:
///倍增算法
const int maxn = 1e5 + 10;
int dis[maxn]; ///与根节点的距离
int dep[maxn]; ///深度
int fa[maxn][25]; /// fa[i][j] 表示 i 节点的第 2^j 的祖先
int n,m; ///其中 m = log(n * 1.0) / log(2.0); n 为总数,编号为 1 ~ n;
struct node{
int v,w;
};
vector<node>edges[maxn*2]; ///存放树,放双向边
void dfs(int f,int s,int w){
if(f != s) dep[s] = dep[f] + 1, dis[s] = dis[f] + w;
for(int j = 0;j < edges[s].size();j ++){
int v = edges[s][j].v;
if(v != f){
fa[v][0] = s;
for(int i = 1;i <= m;i ++)
fa[v][i] = fa[fa[v][i-1]][i-1];
dfs(s,v,edges[s][j].w);
}
}
}
int lca(int u,int v){
if(dep[u] < dep[v]) swap(u,v); ///u 在下面
int d = dep[u] - dep[v];
for(int i = 0; (1 << i) <= d;i ++) ///移动到相同深度
if((1 << i) & d) ///(1<<i)&f找到f化为2进制后1的位置,移动到相应的位置
u = fa[u][i]; ///比如f=5(101),先移动2^0祖先,然后再移动2^2祖先
if(u != v){
for(int i = m;i >= 0;i --)
if(fa[u][i] != fa[v][i]) ///从最大祖先开始,判断a,b祖先,是否相同
u = fa[u][i], v = fa[v][i]; ///如不相同,a b同时向上移动2^j
u = fa[u][0];
}
return u;
}
int main()
{
int n; scanf("%d",&n);
m = log(n*1.0) / log(2.0);
for(int i = 0;i <= n;i ++) edges[i].clear();
dis[1] = 0; dep[1] = 0;
/// 输入树
dfs(1,1,0);
int q; scanf("%d",&q); ///q 次查询
while(q --){
int x,y; scanf("%d%d",&x,&y);
int LCA = lca(x,y);
printf("LCA is %d , and the dis is %d",LCA,dis[x] + dis[y] - 2 * dis[LCA]);
}
return 0;
}