Multiplied by the Method lca (common ancestor)

Multiplied by the Method lca (common ancestor)

Basically each blog will have a reference to the article, one is insufficient to make up, and second, that in itself is what I think the process of learning to find good information

Ideas:

The algorithm is generally thought to the development.

Think of the smallest common ancestor seeking two nodes, we can put the two mentioned in the same level of depth, jump up step by step, until the two nodes have a common ancestor, in accordance with the algorithm flow, which is the least common ancestor.

But if such a step by step up would be too tantalizing, in order to improve the efficiency of it, will no longer be a time jump step, and jump \ (2 ^ i \) steps. Generally, this first bounce jump to node until two flat, two such spring up together.

How to determine how much is appropriate that this i do?

Here we need a pretreatment array f, f [u] [i] to represent the i-th node on behalf of the ancestors of u, f [u] [0] indicates the father of node u, to the array prior to use star chain ( recently how old it) plus dfs to pre-produce,Very convenient. That it what use is it? Used when lifting nodes, we can use this array directly to the appropriate node mention of his ancestors up. How would be suitable ancestor? It is assumed that demand lca (s, t)

Two steps, the first step is the low-high junction node reference point (with respect to the leaf nodes) in depth. At this time a for loop from the 20th-generation ancestor s start (Why is twenty generations? General tree may reach this depth), after the s see if I can put up to meet \ (depth [s]> = depth [ T] \) , can not meet the nineteenth to see this has been to reduce the generation of ancestors i, if satisfied put s post, or continue to decrease i, until the final phase level two nodes. This process has a blog I talked about the very image (see article), likened the process crow drink, it is a large crow first volume (this is the value of i) into the glass of water, and then gradually decrease into items the volume of the cup until the water rose up, rather than filling in things such small particles of sand. This is why i value to be descending.

The second step is to enhance the overall process, we do not know the lifting steps, conditions in circulation becomes \ (IF (F [S] [I]! = F [T] [I]) \) . As long as he is not the same as two ancestors upgrade, ancestors, there are two possibilities, one node may be an ancestor common ancestor but not least, another possibility is to a common ancestor. The former appears at the beginning of the cycle, the more depth you want to start mentioning, so it may be mentioned a common ancestor. Such cases do not control, continue to decrease the value of i maybe later due to meet \ (if \) conditions through the process of upgrading a number of points, the last is not satisfied \ (if \) conditions, indicating two have a minimum common ancestor, this time a random output node of the parent node on the line.

Algorithm is complete, look at the code. See if it appears less clear reference article.

Code:

There are some code optimization, the for loop into a log_2 + 1 array, optimized with regard to this, I do not know what he was optimized in place, it appears from a blog (see article), I used to optimize even suck oxygen and oxygen is not useless to optimize the same ((╯ `□ ') ╯(┻━┻), T are the three points, and finally added to read optimization (see above blog post) only pass.

#include <iostream>
#include <cstdio>
#define max_n 500005
using namespace std;
int n,m,s;//n为结点数,m为边数,s为根节点标号
int lg[max_n];//优化用到的预处理的数组,存log_2[i]-1
int f[max_n][23];//f[u][i]表示结点u的第i代祖先,其中f[u][0]为u的父结点
int depth[max_n];//节点深度
//链式前向星
int head[max_n];
struct edge
{
    int v;
    int next;
}e[max_n<<1];
int cnt = 0;
void add(int u,int v)
{
    ++cnt;
    e[cnt].v = v;
    e[cnt].next = head[u];
    head[u] = cnt;
}
//快速读入模板
inline void read(int& x)
{
    x=0;int f=0;char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') f = 1;ch = getchar();}
    while('0'<=ch&&ch<='9') {x = 10*x+ch-'0';ch=getchar();}
    x = f?-x:x;
}
//dfs预处理出结点往上跳2^i的结点
void dfs(int u,int from)
{
    depth[u] = depth[from]+1;//比父结点深度加一
    for(int i = 1;1<<i<=depth[u];i++)//祖先结点要存在,不存在的默认为零
    {
        f[u][i] = f[f[u][i-1]][i-1];//f[u][i]的第u个结点第i-1代祖先的第i-1代祖先记为第i代祖先
    }
    for(int i = head[u];i;i=e[i].next)
    {
        int v = e[i].v;
        if(v==from) continue;//因为是无向边,判断不能反回父结点
        f[v][0] = u;//
        dfs(v,u);
    }
}
//求最近公共祖先
int lca(int s,int t)
{
    if(depth[s]<depth[t]) swap(s,t);//设s比t深
    while(depth[s]>depth[t])//若s比t深,不断上移s
    {
        s = f[s][lg[depth[s]-depth[t]]-1];//上移log_2[深度差]步,直到相平
    }
    if(s==t)//若t为s的祖先
    {
        return s;//则s,t的lca是t
    }
    for(int i = lg[depth[s]]-1;i>=0;i--)//待s,t相平后
    {
        if(f[s][i]!=f[t][i])//只要两公共祖先不等
        {
            s = f[s][i];//将二者上移
            t = f[t][i];
        }
    }
    /*for(int i = 20;i>=0;i--)
    {
        if(f[s][i]!=f[t][i])
        {
            s = f[s][i];
            t = f[t][i];
        }
    }*/
    return f[s][0];//直到最后两者公共祖先相等,记为lca
}
int main()
{
    read(n);
    read(m);
    read(s);
    for(int i = 1;i<=n;i++)//优化数组玄学构造法求log_2[i]+1
    {
        lg[i] = lg[i-1]+((1<<lg[i-1])==i);
    }
    for(int i = 1;i<n;i++)
    {
        int u,v;
        read(u);
        read(v);
        add(u,v);
        add(v,u);
    }
    dfs(s,0);
    int ans = 0;
    for(int i = 1;i<=m;i++)
    {
        int a,b;
        read(a);
        read(b);
        ans = lca(a,b);
        cout << ans << endl;
    }
    return 0;
}

Reference article:

Li Bai students, doubling the trees seeking Detailed LCA, https://www.cnblogs.com/lbssxz/p/11114819.html (speaking of good speaking good )

· Legend Strategy Ann meditation, problem solution P3379 [[template] recent common ancestor (LCA)], https://www.luogu.org/blog/morslin/solution-p3379 (the source of the mysterious optimization )

Guess you like

Origin www.cnblogs.com/zhanhonhao/p/11299340.html