luogo p3379 【模板】最近公共祖先(LCA)

【模板】最近公共祖先(LCA)

题意

  • 给一个树,然后多次询问(a,b)的LCA

    模板(主要参考一些大佬的模板)

#include<bits/stdc++.h>
//自己的2点:树的邻接链表(静态)表示; lca 的倍增算法
//优化 log[]
const int maxn=500010;
int N,M,S;//S根节点标号
int head[maxn];//head[i]=k 以i为起点的第一条边是edge[k]
int dep[maxn],dp[maxn][21];//dp[i][j] i向上走2^j
int lg[maxn];//(log2(i)+1)

struct edge{
    int v,next;
};
edge egs[maxn<<1];
int k=0;
void getlg(){
    for(int i=1;i<=n;i++){
        lg[i]=lg[i-1]+((1<<lg[i-1])==i);
    }
}
void add(int a,int b){
    //加入边a,b
    egs[k].v=b;
    egs[k].next=head[a];
    head[a]=k++;
}
void dfs(int u,int fa){
    dep[u]=dep[fa]+1;
    //printf("db: u %d fa %d dep %d %d\n",u,fa,dep[u],dep[fa]);
    dp[u][0]=fa;
    for(int i=1;(1<<i)<=dep[u];i++){
        dp[u][i]=dp[dp[u][i-1]][i-1];
    }
    int k;
    for(k=head[u];k!=-1;k=egs[k].next){
        if(egs[k].v!=fa) dfs(egs[k].v,u);
    }

}
int lca(int a,int b){
    if(dep[a]<dep[b]) std::swap(a,b);
    //dep[a]>=dep[b] a 向上走
    for(int j=20;j>=0;j--){
        if(dep[a]-(1<<j)>=dep[b]){
            a=dp[a][j];
        }
    }
    if(a==b) return a;
    //a,b同时向上走
    for(int i=20;i>=0;i--){
        if(dp[a][i]!=dp[b][i]){
            a=dp[a][i];
            b=dp[b][i];
        }
    }
    return dp[a][0];
}

int main(){
    scanf("%d %d %d",&N,&M,&S);
    memset(head,-1,sizeof(head));
    memset(dep,0,sizeof(dep));
    memset(dp,0,sizeof(dp));
    for(int i=1;i<N;i++){
        int a,b;
        scanf("%d %d",&a,&b);
        add(a,b);
        add(b,a);
    }

    dfs(S,0);
    /*
    for(int i=0;i<=N;i++){
        printf("db: %d %d\n",i,dep[i]);
    }
    */
    int x,y;
    for(int i=1;i<=M;i++){
        scanf("%d  %d",&x,&y);
        printf("%d\n",lca(x,y));
    }
    return 0;

}

加了lg[]数组优化的(略微快一点)

#include<bits/stdc++.h>
//自己的2点:树的邻接链表(静态)表示; lca 的倍增算法
//优化 log[]
const int maxn=500010;
int N,M,S;//S根节点标号
int head[maxn];//head[i]=k 以i为起点的第一条边是edge[k]
int dep[maxn],dp[maxn][21];//dp[i][j] i向上走2^j
int lg[maxn];//(log2(i)+1)

struct edge{
    int v,next;
};
edge egs[maxn<<1];
int k=0;
void getlg(){
    for(int i=1;i<=N;i++){
        lg[i]=lg[i-1]+((1<<lg[i-1])==i);
    }
}
void add(int a,int b){
    //加入边a,b
    egs[k].v=b;
    egs[k].next=head[a];
    head[a]=k++;
}
void dfs(int u,int fa){
    dep[u]=dep[fa]+1;
    //printf("db: u %d fa %d dep %d %d\n",u,fa,dep[u],dep[fa]);
    dp[u][0]=fa;
    for(int i=1;i<=(lg[dep[u]]-1);i++){
        dp[u][i]=dp[dp[u][i-1]][i-1];
    }
    int k;
    for(k=head[u];k!=-1;k=egs[k].next){
        if(egs[k].v!=fa) dfs(egs[k].v,u);
    }

}
int lca(int a,int b){
    if(dep[a]<dep[b]) std::swap(a,b);
    //dep[a]>=dep[b] a 向上走
    while(dep[a]>dep[b]){
        a=dp[a][lg[dep[a]-dep[b]]-1];
    }
    if(a==b) return a;
    //a,b同时向上走
    for(int i=(lg[dep[a]]-1);i>=0;){
        if(dp[a][i]!=dp[b][i]){
            a=dp[a][i];
            b=dp[b][i];
            i=lg[dep[a]];
        }
        else i--;
    }
    return dp[a][0];
}

int main(){
    scanf("%d %d %d",&N,&M,&S);
    memset(head,-1,sizeof(head));
    memset(dep,0,sizeof(dep));
    memset(dp,0,sizeof(dp));
    getlg();
    for(int i=1;i<N;i++){
        int a,b;
        scanf("%d %d",&a,&b);
        add(a,b);
        add(b,a);
    }

    dfs(S,0);
    /*
    for(int i=0;i<=N;i++){
        printf("db: %d %d\n",i,dep[i]);
    }
*/
    int x,y;
    for(int i=1;i<=M;i++){
        scanf("%d  %d",&x,&y);
        printf("%d\n",lca(x,y));
    }
    return 0;

}

值得注意的问题

  • 初始化的位置
  • 树的邻接链表表示(真的比较省内存而且好用)
  • lca的倍增算法(还可以求树上两点距离)

原文地址:https://www.cnblogs.com/fridayfang/p/9532296.html

时间: 2024-10-09 22:42:09

luogo p3379 【模板】最近公共祖先(LCA)的相关文章

[模板]最近公共祖先LCA

本人水平有限,题解不到为处,请多多谅解 本蒟蒻谢谢大家观看 题目:传送门 倍增求LCA模板 code: #include<bits/stdc++.h> #pragma GCC optimize(3) using namespace std; int n,q,a,b,tot,m; int nxt[1000010],head[1000010],ver[1000010],dep[1000010],f[1000010][21]; //设f[x,k]表示x的2^k辈祖先,即从x向根节点走2^k步到达的节

树链剖分 [模板]最近公共祖先LCA

本人水平有限,题解不到为处,请多多谅解 本蒟蒻谢谢大家观看 题目:传送门 树链剖分:跑两遍dfs,第一遍找重边,第二遍找重链. 重儿子:父亲节点的所有儿子中子树结点数目最多(size最大)的结点: 轻儿子:父亲节点中除了重儿子以外的儿子: 重边:父亲结点和重儿子连成的边: 轻边:父亲节点和轻儿子连成的边: 重链:由多条重边连接而成的路径: 轻链:由多条轻边连接而成的路径 son[]表示重儿子,top[]表示重链所在的第一个节点,sz[]表示子节点数,fa[]表示父亲节点 图示: code: #i

最近公共祖先(LCA)问题

描述 对于有根树T的两个节点u和v,最近公共祖先LCA(T,u,v)表示一个节点x满足x是u,v的公共祖先且x的深度尽可能大. 算法 求解LCA问题主要有三种解法,分别是暴力搜索,Tanjar算法,最后一种是转化为RMQ问题,用DFS+ST算法来求解 暴力搜索 如果数据量不大的时候可以采用暴力搜索法.先将节点u的祖先节点全部标记出来,然后顺着节点v沿着父亲节点的方向向上遍历,直到遍历到一个被标记的节点,这个节点即为所求节点.或者分别获取u,v到根节点的路径P1,P2,可以将这两条路径看做两个两个

POJ 1470 Closest Common Ancestors【最近公共祖先LCA】

题目链接:http://poj.org/problem?id=1470 题目大意:给出一棵树,再给出若干组数(a,b),输出节点a和节点b的最近公共祖先(LCA) 就是很裸的LCA,但是我用的是<挑战程序设计竞赛>上的"基于二分搜索的算法求LCA",我看网上用的都是tarjan算法.但是我的代码不知道为什么提交上去 wrong answer,自己想的很多测试数据也都和题解结果一样,不知道错在哪里,所以把代码保存一下,留待以后解决...... 如果读者有什么建议,希望提出来,

最近公共祖先LCA(Tarjan算法)的思考和算法实现——转载自Vendetta Blogs

最近公共祖先LCA(Tarjan算法)的思考和算法实现 LCA 最近公共祖先 Tarjan(离线)算法的基本思路及其算法实现 小广告:METO CODE 安溪一中信息学在线评测系统(OJ) //由于这是第一篇博客..有点瑕疵...比如我把false写成了flase...看的时候注意一下! //还有...这篇字比较多 比较杂....毕竟是第一次嘛 将就将就 后面会重新改!!! 首先是最近公共祖先的概念(什么是最近公共祖先?): 在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先

【C++】最近公共祖先LCA(Tarjan离线算法)&amp;&amp; 洛谷P3379LCA模板

1.前言 首先我们介绍的算法是LCA问题中的离线算法-Tarjan算法,该算法采用DFS+并查集,再看此算法之前首先你得知道并查集(尽管我相信你如果知道这个的话肯定是知道并查集的),Tarjan算法的优点在于相对稳定,时间复杂度也比较居中,也很容易理解(个人认为). 2.思想 下面详细介绍一下Tarjan算法的思想: 1.任选一个点为根节点,从根节点开始. 2.遍历该点u所有子节点v,并标记这些子节点v已被访问过. 3.若是v还有子节点,返回2,否则下一步. 4.合并v到u上. 5.寻找与当前点

HDU 2586 How far away ?(LCA模板 近期公共祖先啊)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2586 Problem Description There are n houses in the village and some bidirectional roads connecting them. Every day peole always like to ask like this "How far is it if I want to go from house A to house

HDU 2586 How far away ?(LCA模板 最近公共祖先啊)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2586 Problem Description There are n houses in the village and some bidirectional roads connecting them. Every day peole always like to ask like this "How far is it if I want to go from house A to house

最近公共祖先LCA【模板】

Tarjan-LCA算法: 对于每一点u: 1.建立以u为代表元素的集合. 2.遍历与u相连的节点v,如果没有被访问过,对于v使用Tarjan-LCA算法,结束后,将v的集合并入u的集合. 3.对于与节点u相关的询问(u,v),如果v被访问过,则结果就是v所在集合的所代表的元素. 求(u,v)的最近公共祖先节点,则询问时调用QEdges[k].lca = find(QEdges[k].to); 求(u,v)在树上的距离,Dist(u,v) = Dist(1,u) + Dist(1,v) - 2*

最近公共祖先lca模板

void dfs(int x,int root){//预处理fa和dep数组 fa[x][0]=root; dep[x]=dep[root]+1; for(int i=1;(1<<i)<dep[x];i++) fa[x][i]=fa[fa[x][i-1]][i-1]; for(int i=0;i<ve[x].size();i++)dfs(ve[x][i],x); } int lca(int a,int b){//计算两个节点的最近公共祖先 if(dep[a]>dep[b])s