【转载地址】点击打开链接

LCA:
什么是LCA?
Lowest Common Ancestor, 指的是树上两点的最近公共祖先。
有了它, 我们可以高效地求解树上两点间的距离、最大权值边等信息。
的时间复杂度:
暴力 O(n+m+qn)
倍增法O(n+m+nlogn+qlogn)
欧拉序列与RMQ O(n+m+nlogn+q)
Tarjan离线算法 O(n+m+q)
树链剖分 O(n+m+qlogn)
lca的代码模版
暴力与倍增法很好理解,对于欧拉序列与RMQ,Tarjan离线算法,其实都是利用了树的欧拉序列
 欧拉序列即是树上两点路径经过点的序列,所以才能正确的求出lca(那个深度最小的点)

//brute force
vector<int> G[N];
int fa[N], dep[N];

void dfs(int u, int f, int d) {
    fa[u] = f;
    dep[u] = d;
    for(int i = 0; i < G[u].size(); ++i) {
        int v = G[u][i];
        if(v == f) continue;
        dfs(v, u, d + 1);
    }
}

void init() {
    dfs(root, -1, 0);
}

int lca(int u, int v) {
    if(dep[u] > dep[v]) swap(u, v);
    while(dep[v] > dep[u]) v = fa[v];
    while(u != v) {
        u = fa[u];
        v = fa[v];
    }
    return u;
}

//doubling
vector<int> G[N];
const int LOG = 18;
int p[LOG][N], dep[N];

void dfs(int u, int f, int d) {
    dep[u] = d;
    p[0][u] = f;
    for(int i = 0; i < G[u].size(); ++i) {
        int v = G[u][i];
        if(v == f) continue;
        dfs(v, u, d + 1);
    }
}

void init() {
    dfs(root, -1, 0);
    for(int i = 0; i + 1 < LOG; ++i)
        for(int v = 1; v <= n; ++v)
            if(p[i][v] < 0) p[i + 1][v] = -1;
            else p[i + 1][v] = p[i][p[i][v]];
}

int lca(int u, int v) {
    if(dep[u] > dep[v]) swap(u, v);
    for(int i = 0; i < LOG; ++i)
        if(dep[v] - dep[u] >> i & 1)
            v = p[i][v];
    if(u == v) return u;
    for(int i = LOG - 1; ~i; --i) {
        if(p[i][u] != p[i][v]) {
            u = p[i][u];
            v = p[i][v];
        }
    }
    return p[0][u];
}

//Euler Sequence & RMQ(ST)
int getMin(int x, int y) {
    return dep[x] < dep[y] ? x : y;
}

struct SparseTable {
    int dp[20][N << 1];
    void init(int n) {
        for(int i = 1; i <= n; ++i) dp[0][i] = i;
        for(int i = 1; (1 << i) <= n; ++i)
            for(int j = 1; j + (1 << i) - 1 <= n; ++j)
                dp[i][j] = getMin(dp[i - 1][j],
                                  dp[i - 1][j + (1 << i - 1)]);
    }
    int RMQ(int l, int r) {
        int k = 31 - __builtin_clz(r - l + 1);
        return getMin(dp[k][l], dp[k][r - (1 << k) + 1]);
    }
} st;

vector<int> G[N];
int vs[N << 1], dep[N << 1], first[N], dfsNum;

void dfs(int u, int f, int d) {
    vs[++dfsNum] = u;
    dep[dfsNum] = d;
    first[u] = dfsNum;
    for(int i = 0; i < G[u].size(); ++i) {
        int v = G[u][i];
        if(v == f) continue;
        dfs(v, u, d + 1);
        vs[++dfsNum] = u;
        dep[dfsNum] = d;
    }
}

void init() {
    dfsNum = 0;
    dfs(root, -1, 0);
    st.init(2 * n - 1);
}

int lca(int u, int v) {
    if(first[u] > first[v]) swap(u, v);
    int idx = st.RMQ(first[u], first[v]);
    return vs[idx];
}

//Tarjan-lca & Disjoint Set
struct Query {
    int v, id;
};
vector<Query> Q[N];

int ans[MAXQ], ancestor[N], vis[N];

int find(int x) {
    return ancestor[x] = ancestor[x] == x ? x : find(ancestor[x]);
}

vector<int> G[N];
void tarjan(int u) {
    vis[u] = true;
    ancestor[u] = u;
    for(int i = 0; i < G[u].size(); ++i) {
        int v = G[u][i];
        if(vis[v]) continue;
        tarjan(v);
        ancestor[v] = u;
    }
    for(int i = 0; i < Q[u].size(); ++i) {
        Query& q = Q[u][i];
        if(vis[q.v]) ans[q.id] = find(q.v);
    }
}

//HLD-lca
int lca(int u, int v) {
    int ret;
    while(true) {
        if(top[u] == top[v]) {
            ret = dep[u] < dep[v] ? u : v;
            break;
        } else if(dep[top[u]] > dep[top[v]])
            u = fa[top[u]];
        else v = fa[top[v]];
    }
    return ret;
}

LCA 题目特点
利用倍增法dp的题目居多
嵌套数据结构动态查询
利用lca树上暴力