zoukankan      html  css  js  c++  java
  • Tarjan求LCA

    LCA问题算是一类比较经典的树上的问题

    做法比较多样

    比如说暴力啊,倍增啊等等

    今天在这里给大家讲一下tarjan算法!

    tarjan求LCA是一种稳定高速的算法

    时间复杂度能做到预处理O(n + m),查询O(1)

    它的主要思想是dfs和并查集

    1.输入数据,找出根节点(或输入的)并将图存起来

    2.输入需要查找的每一对点(两个点),也存起来(也存成图)

    3.从根节点开始向它的每一个孩子节点进行深搜

    4.同时开一个bool类型的数组记录此节点是否搜索过

    5.搜索到p节点时先将p标记为已经搜索过了

    6.然后遍历所有与p相连的节点,并标记为已经搜索过了

    7.接着将p的子节点和p合并(此处要用到并查集)

    8.然后遍历所有和p有询问关系的p的子节点

    9.若该子节点已经遍历过,则一定可以将该子节点和p的父亲节点合并

    可能还是有很多人并没有完全理解这段文字叙述的算法过程

    下面就直接上代码(注释很详细)

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cctype>
    #include<cmath>
    #include<string>
    #include<iostream>
    #include<algorithm>
    #include<map>
    #include<set>
    #include<queue>
    #include<stack>
    #include<vector>
    using namespace std;
    typedef long long ll;
    const int maxn = 5e5 + 5;
    int read()
    {
        int ans = 0, op = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9')
        {
            if(ch == '-') op = -1;
            ch = getchar();
        }
        while(ch >= '0' && ch <= '9')
        {
            ans *= 10;
            ans += ch - '0';
            ch = getchar();
        }
        return ans * op;
    }
    struct Drug
    {
        int next, to, lca;
    }edge[maxn<<1], qedge[maxn<<1];//edge[N]为树的链表;qedge[N]为需要查询LCA的两节点的链表
    int n, m, s, x, y;
    int num_edge, num_qedge, head[maxn], qhead[maxn], father[maxn];
    bool visit[maxn];//判断是否被找过 
    void add_edge(int from, int to)//建立树的链表 
    {
        edge[++num_edge].next = head[from];
        edge[num_edge].to = to;
        head[from] = num_edge;
    //    printf("#%d #%d #%d #%d
    ", num_edge, head[from], from, edge[num_edge].next);
    }
    void add_qedge(int from, int to)//建立需要查询LCA的两节点的链表 
    {
        qedge[++num_qedge].next = qhead[from];
        qedge[num_qedge].to = to;
        qhead[from] = num_qedge;
    }
    int find(int x)//找爹函数 
    {
        if(father[x] ^ x) father[x] = find(father[x]);
        return father[x];
    }
    void dfs(int x)//把整棵树的一部分看作以节点x为根节点的小树, x的初始值为s; 
    {
        father[x] = x;//由于节点x被看作是根节点,所以把x的father设为它自己 
        visit[x] = 1;//标记为已被搜索过 
        for(int k = head[x]; k ; k=edge[k].next)//遍历所有与x相连的节点 
        {
            if(!visit[edge[k].to])//若未被搜索 
            {
                dfs(edge[k].to);//以该节点为根节点搞小树 
                father[edge[k].to] = x;//把x的孩子节点的father重新设为x 
            }
        }
        for(int k = qhead[x]; k ; k = qedge[k].next)//搜索包含节点x的所有询问 
        {
            if(visit[qedge[k].to])//如果另一节点已被搜索过 
            {
                qedge[k].lca = find(qedge[k].to);
                //把另一节点的祖先设为这两个节点的最近公共祖先 
                if(k & 1) qedge[k + 1].lca = qedge[k].lca;
                //由于将每一组查询变为两组,所以2n-1和2n的结果是一样的
                else qedge[k - 1].lca = qedge[k].lca;
            }
        }
    }
    int main(){
        n = read(), m = read(), s = read();//输入节点数,查询数和根节点 
        for(int i = 1;i < n;i++)
        {
            x = read(), y = read();//输入每条边 
            add_edge(x, y);
            add_edge(y, x);
        }
        for(int i = 1;i <= m;i++)
        {
            x = read(), y = read();
            //输入每次查询,考虑(u,v)时若查找到u但v未被查找,所以将(u,v)(v,u)全部记录 
            add_qedge(x, y);
            add_qedge(y, x);
        }
        dfs(s); 
        for(int i = 1;i <= m;i++) printf("%d
    ", qedge[i << 1].lca);//两者结果一样,只输出一组即可
    //    printf("%d", num_edge); 
        return 0;
    }

     

  • 相关阅读:
    第九次作业
    第八次作业
    第七次作业
    组合数学—递推关系与母函数
    组合数学—排列组合
    三角函数
    OpenCV初步
    计算机视觉如何入门
    GDB调试技巧:总结篇
    PyQt5之窗口类型
  • 原文地址:https://www.cnblogs.com/thx666/p/9767910.html
Copyright © 2011-2022 走看看