倍增\ tarjan求lca

对于每个节点v,记录anc[v][k],表示从它向上走2k步后到达的节点(如果越过了根节点,那么anc[v][k]就是根节点)。

dfs函数对树进行的dfs,先求出anc[v][0],再利用anc[v][k] = anc[anc[v][k - 1]][k - 1]  (从v向上2k步即为从v向上2(k - 1)步再向上2(k - 1)步)

求出其他anc[v][k]的值

lca(u, v)函数寻找u和v的lca, 首先把u和v调整到一个高度。如果此时u和v重合,那么这就是我们要找的lca,如果他们补充和,就不断的寻找一个最小的k,使得

anc[u][k] = anc[v][k]

 

int anc[maxn][20], deep[maxn];

int dfs(int u, int fa)
{
    for(int i = 1; i < 20; i++)
        anc[u][i] = anc[anc[u][i - 1]][i - 1];
    for(int i = head2[u]; i != -1; i = Edge[i].next)
    {
        int v = Edge[i].v;
        if(v == fa || deep[v]) continue;
        anc[v][0] = u;
        deep[v] = deep[u] + 1;
        dfs(v, u);
    }
}

int lca(int u, int v)
{
    if(deep[u] < deep[v]) swap(u, v);
    for(int i = 20 - 1; i >= 0; i--)
        if(deep[anc[u][i]] >= deep[v])
            u = anc[u][i];

    for(int i = 20 - 1; i >= 0; i--)
    {
        if(anc[u][i] != anc[v][i])
        {
            u = anc[u][i];
            v = anc[v][i];
        }
    }
    if(u == v) return u;
    return anc[u][0];
}

 

 

tarjan求lca  

1.任选一个点为根节点,从根节点开始。

2.遍历该点u所有子节点v,并标记这些子节点v已被访问过。

3.若是v还有子节点,返回2,否则下一步。

4.合并v到u上。

5.寻找与当前点u有询问关系的点v。

合并就用并查集就好了

 

板子先欠着

 

      6.若是v已经被访问过了,则可以确认u和v的最近公共祖先为v被合并到的父亲节点a。

posted @ 2019-04-18 19:54  WTSRUVF  阅读(148)  评论(0编辑  收藏  举报