LCA模板 ( 最近公共祖先 )

LCA 有几种经典的求取方法、这里只给出模板,至于原理不详叙。

1、RMQ转LCA

大致就是 DFS求出欧拉序 => 对欧拉序做ST表 => LCA(u, v) 即为 u、v 最先出现在欧拉序中的编号之间的最小值。

因为 LCA 的子树中必定有一个节点是 u,一个是 v,而且必定在两个节点到根节点的唯一路径上。

例如有欧拉序列 1 2 1 3 4 3 1 则 LCA(2, 3) == 1 、首次出现 2 的下标是 2、首次出现 3 的下标是 4、则 LCA 就是下标 2~4 之间的最小值即 1

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e4 + 10;///顶点数
struct EDGE{ int v, nxt, w; }Edge[maxn<<1];
int Head[maxn], cnt;
int n, q, s;///点数、问询数、dfs起点
int fp[maxn]; int dfsLen;///每个顶点在欧拉序中第一次出现的位置、DFS序的长度(用做时间戳)
int id[maxn]; int idLen;///每个顶点在DFS序中访问次序(用于离散化)、id数组的长度
int dp[maxn<<1][21];///跑ST表的dp数组(第二维应开到 ceil(log2(maxn<<1)) )

inline void init()
{
    memset(dp, 0, sizeof(dp));
    memset(Head, -1, sizeof(Head));
    cnt = 0; idLen = dfsLen = 0;
}

inline void AddEdge(int from, int to, int weight)
{
    Edge[cnt].v = to;
    Edge[cnt].nxt = Head[from];
    Head[from] = cnt++;
}


void dfs(int v, int Fa)
{
    int tmp;
    id[tmp = ++idLen] = v;
    dp[fp[v] = ++dfsLen][0] = tmp;
    for(int i=Head[v]; i!=-1; i=Edge[i].nxt){
        int Eiv = Edge[i].v;
        if(Eiv == Fa) continue;
        dfs(Eiv, v, d);
        dp[++dfsLen][0] = tmp;
    }
}

void GetST()
{
    for(int j=1; (1<<j)<=dfsLen; j++){
        for(int i=1; i+(1<<j)-1<=dfsLen; i++){
            dp[i][j] = min(dp[i][j-1], dp[i+(1<<(j-1))][j-1]);
        }
    }
}

int LCA(int u, int v)
{
    if(fp[u] > fp[v]) swap(u, v);
    int L = fp[u], R = fp[v];
    int k = (int)(log(R-L+1)/log(2));
    return id[min(dp[L][k], dp[R-(1<<k)+1][k])];
}

int main(void)
{
    scanf("%d %d %d", &n, &s, &q);
    for(int i=1; i<n; i++){
        int u, v;
        scanf("%d %d", &u, &v);
        AddEdge(u, v);
        AddEdge(v, u);
    }

    dfs(s, -1);
    GetST();

    while(q--){
        int u, v;
        scanf("%d %d", &u, &v);
        printf("%d\n", LCA(u, v));
    }
    return 0;
}
View Code

2、树上倍增

3、Tarjan算法( 离线 )

猜你喜欢

转载自www.cnblogs.com/Rubbishes/p/9126188.html
今日推荐