最近公共祖先:在有根树的结构中,对于任意两个节点,它们的最近公共祖先是指满足下面两个条件的节点:
  1. 同时是公共祖先(即到根节点的路径上,也在到根节点的路径上)、
  2. 是所有公共祖先中深度最大的节点("最近"的本质是距离u 和 v距离最近)
关键性质:
  1. 到根节点的路径与到根节点的路径中第一个交点
  2. 这棵树里面的的最短路径一定经过并且是这条最短路径中深度最小的节点
  3. 多个节点的最近公共祖先
  4. 如果的祖先,则LCA(u,v) =u;反之同理
的常见使用场景:
  1. 计算树上两点间的距离,很好理解:表示根节点到的距离,表示根节点到的距离,两者重叠部分是根节点到的距离,并且重复计算了两次,因此需要减去
  2. 可以判断树中的关系,判断节点是否在的子树中,如果,则的子树中
  3. 很多树上问题需要对任意两点之间的路径经行操作,(如查询路径的最大值,最小值,求和等),而LCA是将路径拆分为两段的关键
  4. 辅助树形DP:在树形DP中,经常需要统计跨字数的路径信息,LCA可以帮助确定路径的公共祖先,从而划分状态转移范围
LCA模板:
定义表示节点级祖先(节点的1级祖先就是它的父节点,2级祖先就是父节点的父节点.......)
所以
初始化每一个就是节点的直接父节点,可以通过对整棵树的DFS求出每个节点的深度以及直接父节点:
void dfs(int u, int fa, int dep){
    depth[u] = dep;
    st[u][0] = fa;
    for(int v : adj[u]){
        if(v != fa) dfs(v, u, dep + 1);
    }
}
然后就是如何求任意两个节点的最近公共最先了:
首先节点的深度可能不一样,为了方便处理,将深度大的设置为节点
if(depth[u] < depth[v]) swap(u, v);
然后需要将节点的深度慢慢减小深度至和相同:
int dif = depth[u] - depth[v];
for(int i = M - 1; i >= 0; i --){
    if((dif >> i) & 1) u = st[u][i];
}
现在的深度变为相同了,如果此时的值相同,那么直接返回节点,表示一开始的节点是在节点的子树里面(但是现在的的值相同,所以返回节点都可以)
如果的值不相同,那么现在就是在同一深度,我们就可以慢慢的同时减少深度,如果某个时满足,说明的第级祖先相同,即找到了一个公共祖先,但不一定是升读最大的,如果,那么我继续减少深度,最终的局面是,即的直接父亲相同,那么这个就是
总代码:
#include<bits/stdc++.h>
using namespace std;

#define endl '\n'
#define int long long
#define IOS ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
#define HelloWorld IOS;


const int N = 5e5 + 10;
const int M = 20;

int n, m, s;
int st[N][M];
int depth[N];
vector<int> adj[N];

void dfs(int u, int fa, int dep){
    depth[u] = dep;
    st[u][0] = fa;
    for(int v : adj[u]){
        if(v != fa) dfs(v, u, dep + 1);
    }
}

int lca(int u, int v){
    if(depth[u] < depth[v]) swap(u, v);
    int dif = depth[u] - depth[v];
    for(int i = M - 1; i >= 0; i --){
        if((dif >> i) & 1) u = st[u][i];
    }
    if(u == v) return u;
    for(int i = M - 1; i >= 0; i --){
        if(st[u][i] != st[v][i]){
            u = st[u][i];
            v = st[v][i];
        }
    }
    return st[u][0];
}
signed main(){
    HelloWorld;
    
    cin >> n >> m >> s;
    for(int i = 1; i <= n - 1; i ++){
        int u, v; cin >> u >> v;
        adj[u].push_back(v);
        adj[v].push_back(u);
    }    
    dfs(s, s, 1);
    for(int j = 1; j < M; j ++){
        for(int i = 1; i <= n; i ++) st[i][j] = st[st[i][j - 1]][j - 1];
    }
    while(m --){
        int u, v; cin >> u >> v;
        cout << lca(u, v) << endl;
    }
    return 0;
}