码迷,mamicode.com
首页 > 其他好文 > 详细

最近公共祖先问题(LCA)模板

时间:2015-09-21 19:37:31      阅读:156      评论:0      收藏:0      [点我收藏+]

标签:

最近公共祖先问题(LCA)是求一颗树上的某两点距离他们最近的公共祖先节点,由于树的特性,树上两点之间路径是唯一的,所以对于很多处理关于树的路径问题的时候为了得知树两点的间的路径,LCA是几乎最有效的解法。

首先是LCA的倍增算法。算法主体是依靠首先对整个树的预处理DFS,用来预处理出每个点的直接父节点,同时可以处理出每个点的深度和与根节点的距离,然后利用类似RMQ的思想处理出每个点的 2 的幂次的祖先节点,这就可以用 nlogn 的时间完成整个预处理的工作。然后每一次求两个点的LCA时只要对两个点深度经行考察,将深度深的那个利用倍增先爬到和浅的同一深度,然后一起一步一步爬直到爬到相同节点,就是LCA了。

具体模板是从鹏神的模板小改来的。

注释方便理解版:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<algorithm>
 4 using namespace std;
 5 
 6 const int maxn=1e5+5;
 7 const int maxm=1e5+5;
 8 const int maxl=20;        //总点数的log范围,一般会开稍大一点
 9 
10 int fa[maxl][maxn],dep[maxn],dis[maxn];        //fa[i][j]是j点向上(不包括自己)2**i 层的父节点,dep是某个点的深度(根节点深度为0),dis是节点到根节点的距离
11 int head[maxn],point[maxm],nxt[maxm],val[maxm],size;
12 int n;
13 
14 void init(){
15     size=0;
16     memset(head,-1,sizeof(head));
17 }
18 
19 void add(int a,int b,int v){
20     point[size]=b;
21     val[size]=v;
22     nxt[size]=head[a];
23     head[a]=size++;
24     point[size]=a;
25     val[size]=v;
26     nxt[size]=head[b];
27     head[b]=size++;
28 }
29 
30 void Dfs(int s,int pre,int d){        //传入当前节点标号,父亲节点标号,以及当前深度
31     fa[0][s]=pre;                    //当前节点的上一层父节点是传入的父节点标号
32     dep[s]=d;
33     for(int i=head[s];~i;i=nxt[i]){
34         int j=point[i];
35         if(j==pre)continue;
36         dis[j]=dis[s]+val[i];
37         Dfs(j,s,d+1);
38     }
39 }
40 
41 void Pre(){
42     dis[1]=0;
43     Dfs(1,-1,0);
44     for(int k=0;k+1<maxl;++k){        //类似RMQ的做法,处理出点向上2的幂次的祖先。
45         for(int v=1;v<=n;++v){
46             if(fa[k][v]<0)fa[k+1][v]=-1;
47             else fa[k+1][v]=fa[k][fa[k][v]];    //处理出两倍距离的祖先
48         }
49     }
50 }
51 
52 int Lca(int u,int v){
53     if(dep[u]>dep[v])swap(u,v);        //定u为靠近根的点
54     for(int k=maxl-1;k>=0;--k){
55         if((dep[v]-dep[u])&(1<<k))    //根据层数差值的二进制向上找v的父亲
56             v=fa[k][v];
57     }
58     if(u==v)return u;                //u为v的根
59     for(int k=maxl-1;k>=0;--k){
60         if(fa[k][u]!=fa[k][v]){        //保持在相等层数,同时上爬寻找相同父节点
61             u=fa[k][u];
62             v=fa[k][v];
63         }
64     }
65     return fa[0][u];                //u离lca只差一步
66 }

木有注释版:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;

const int maxn=1e5+5;
const int maxm=1e5+5;
const int maxl=20;

int fa[maxl][maxn],dep[maxn],dis[maxn];
int head[maxn],point[maxm],nxt[maxm],val[maxm],size;
int n;

void init(){
    size=0;
    memset(head,-1,sizeof(head));
}

void add(int a,int b,int v){
    point[size]=b;
    val[size]=v;
    nxt[size]=head[a];
    head[a]=size++;
    point[size]=a;
    val[size]=v;
    nxt[size]=head[b];
    head[b]=size++;
}

void Dfs(int s,int pre,int d){
    fa[0][s]=pre;
    dep[s]=d;
    for(int i=head[s];~i;i=nxt[i]){
        int j=point[i];
        if(j==pre)continue;
        dis[j]=dis[s]+val[i];
        Dfs(j,s,d+1);
    }
}

void Pre(){
    dis[1]=0;
    Dfs(1,-1,0);
    for(int k=0;k+1<maxl;++k){
        for(int v=1;v<=n;++v){
            if(fa[k][v]<0)fa[k+1][v]=-1;
            else fa[k+1][v]=fa[k][fa[k][v]];
        }
    }
}

int Lca(int u,int v){
    if(dep[u]>dep[v])swap(u,v);
    for(int k=maxl-1;k>=0;--k){
        if((dep[v]-dep[u])&(1<<k))
            v=fa[k][v];
    }
    if(u==v)return u;
    for(int k=maxl-1;k>=0;--k){
        if(fa[k][u]!=fa[k][v]){
            u=fa[k][u];
            v=fa[k][v];
        }
    }
    return fa[0][u];
}

 

最近公共祖先问题(LCA)模板

标签:

原文地址:http://www.cnblogs.com/cenariusxz/p/4826875.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!