LCA_最近公共祖先

  LCA 最近公共祖先
  在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先,就是两个节点在这棵树上深度最大的公共的祖先节点。
  换句话说,就是两个点在这棵树上距离最近的公共祖先节点。
  对于一棵有权树,假设u,v两点的最近公共祖先lca(v),若dis[u]表示根节点到u的长度, 则uv间的距离为dis=dis[u]+dis[v]-2*dis[lca(v)]
有人可能会问:那他本身或者其父亲节点是否可以作为祖先节点呢?
    答案是肯定的,很简单,按照人的亲戚观念来说,你的父亲也是你的祖先,而LCA还可以将自己视为祖先节点
    举个例子吧,如下图所示最近公共祖先是2最近公共祖先最近公共祖先。 

LCA_最近公共祖先_第1张图片

求LCA主要有两种方法:离线的DFS+并查集在线的RMQ算法
  这里简单说一说在线和离线的区别:在计算机科学中,一个在线算法是指它可以以序列化的方式一个个的处理输入,也就是说在开始时并不需要已经知道所有的输入。相对的,对于一个离线算法,在开始时就需要知道问题的所有输入数据,而且在解决一个问题后就要立即输出结果。

( 一 )离线算法:tarjan和并查集

Code From Wikipedia

 function TarjanOLCA(u)
     u.ancestor := u;
     for each v in u.children do
         TarjanOLCA(v);
         Union(u,v);
         Find(u).ancestor := u;
     u.color := black;
     for each v such that {u,v} in P do
         if v.color == black
             print "Tarjan's Lowest Common Ancestor of " + u +
                   " and " + v + " is " + Find(v).ancestor + ".";

其中,根节点是可以随意选择的

How far away ?
题意:
给定一棵带权树,求u,v两节点的路径长度
题解:
找到u,v两点的最近公共祖先lca(v)
若dis[u]表示根节点到u的长度,则dis=dis[u]+dis[v]-2*dis[lca(v)]

#include
#include 
#include
using namespace std;
const int MAXN=40010;
const int BLACK=1,WHITE=0;
int father[MAXN],vis[MAXN],dis[MAXN],color[MAXN];
int parent(int u)
{
    while(u!=father[u])
    {
        father[u]=father[father[u]];
        u=father[u];
    }
    return u;
}
void connect(int u,int v)//v作为u的儿子
{
    int fu=parent(u);
    int fv=parent(v);
    father[fv]=fu;
}
struct Node
{
    int to,next,val;
};
Node edge[MAXN<<1];
int head[MAXN],cnt;
void addEdge(int u,int v,int val)
{
    edge[cnt].to=v;
    edge[cnt].val=val;
    edge[cnt].next=head[u];
    head[u]=cnt++;
    edge[cnt].to=u;
    edge[cnt].val=val;
    edge[cnt].next=head[v];
    head[v]=cnt++;
}
struct Ask
{
    int u,to,next,lca;
};
Ask arr[MAXN<<1];
int ask_head[MAXN],idx;
void addAsk(int u,int v)
{
    arr[idx].u=u;arr[idx].to=v;arr[idx].next=ask_head[u];ask_head[u]=idx++;
    arr[idx].u=v;arr[idx].to=u;arr[idx].next=ask_head[v];ask_head[v]=idx++;
}
void tarjan(int u)
{
    vis[u]=1;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        if(vis[v]==0)
        {
            dis[v]=dis[u]+edge[i].val;
            tarjan(v);
            connect(u,v);
        }
    }
    color[u]=BLACK;
    for(int i=ask_head[u];i!=-1;i=arr[i].next)
    {
        int v=arr[i].to;
        if(color[v]==BLACK)
        {
            int fa=parent(v);
            arr[i].lca=fa;
            arr[i^1].lca=fa;
        }
    }
}
int main()
{
    int t,n,m,u,v,val;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        memset(head,-1,sizeof(head));
        memset(ask_head,-1,sizeof(ask_head));
        memset(vis,0,sizeof(vis));
        memset(color,WHITE,sizeof(color));
        for(int i=1;i<=n;i++) father[i]=i;
        cnt=idx=0;
        for(int i=1;i

( 二 )在线算法: RMQ算法
一:LCA和RMQ是可以相互转化的
LCA转RMQ算法是一个在线算法:先用时间去做预处理,然后每读入一个询问,就用很短的时间去回答它,即”问一个答一个,回答时间很短“
预备知识:LCA转为RMQ后,几乎是裸的RMQ问题,RMQ问题,这里推荐ST算法求解,如果不懂ST算法,先学习一下

LCA_最近公共祖先_第2张图片

LCA_最近公共祖先_第3张图片
LCA_最近公共祖先_第4张图片
LCA_最近公共祖先_第5张图片

LCA_最近公共祖先_第6张图片

RMQ算法求LCA的模板如下:

#include
#include
#include
using namespace std;
const int MAXN=500010;
const int MAXE=500010;
struct Node
{
    int to,next,val;
};
Node edge[MAXE];
int head[MAXN],cnt;
void addEdge(int u,int v,int val)
{
    edge[cnt].to=v;
    edge[cnt].val=val;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}
int sequence[MAXN*2];//保存遍历的节点序列,长度为2*n-1,从下标1开始保存
int deep[MAXN*2];//和遍历序列对应的深度数组,长度为2*n-1,从下标1开始保存
int first[MAXN];//每个节点在遍历序列时第一次出现的位置
int dis[MAXN];//每个节点到树根的距离
int dp[MAXN*1][25];//Sparse_table
bool vis[MAXN];//DFS遍历的访问数组
int tot;//序列下标记录
void DFS(int u,int dep)
{
    vis[u]=1;
    sequence[++tot]=u;first[u]=tot;deep[tot]=dep;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        if(vis[v]==0)
        {
            dis[v]=dis[u]+edge[i].val;
            DFS(v,dep+1);
            sequence[++tot]=u;
            deep[tot]=dep;
        }
    }
}
void ST(int len)//index from 1 - len
{
    for(int i=1;i<=len;i++) dp[i][0]=i;

    for(int j=1;(1<y) swap(x,y);
    return sequence[RMQ(x,y)];
}
int main()
{
    int t,n,m,u,v,val;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        memset(head,-1,sizeof(head));
        memset(vis,0,sizeof(vis));
        cnt=tot=0;
        for(int i=1;i

类似的,这里给出带有pos数组的RMQ算法

#include
#include
#include
using namespace std;
const int MAXN=40010;
const int MAXE=MAXN*2;
struct Node
{
    int to,next,val;
};
Node edge[MAXE];
int head[MAXN],cnt;
void addEdge(int u,int v,int val)
{
    edge[cnt].to=v;
    edge[cnt].val=val;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}
int sequence[MAXN*2];//保存遍历的节点序列,长度为2*n-1,从下标1开始保存
int deep[MAXN*2];//和遍历序列对应的深度数组,长度为2*n-1,从下标1开始保存
int first[MAXN];//每个节点在遍历序列时第一次出现的位置
int dis[MAXN];//每个节点到树根的距离
int dp[MAXN*2][25];//Sparse_table
int pos[MAXN*2][25];
bool vis[MAXN];//DFS遍历的访问数组
int tot;//序列下标记录
int temp[MAXN*2];
void DFS(int u,int dep)
{
    vis[u]=1;
    sequence[++tot]=u;first[u]=tot;deep[tot]=dep;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        if(vis[v]==0)
        {
            dis[v]=dis[u]+edge[i].val;
            DFS(v,dep+1);
            sequence[++tot]=u;
            deep[tot]=dep;
        }
    }
}
void ST(int len)//index from 1 - len
{
    for(int i=1;i<=len;i++) dp[i][0]=deep[i];
    for(int i=1;i<=len;i++) pos[i][0]=i;

    for(int j=1;(1<y) swap(x,y);
    return sequence[RMQ(x,y)];
}
int main()
{
    int t,n,m,u,v,val;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        memset(head,-1,sizeof(head));
        memset(vis,0,sizeof(vis));
        cnt=tot=0;
        for(int i=1;i

Design the city
题意:
求树的三点距离
题解:
u,v,w三点距离其实等于(dis(u,v)+dis(u,w)+dis(v,w))/2;
方法一:离线算法:tarjan和并查集

#include
#include 
#include
using namespace std;
const int MAXN=50010;
const int BLACK=1,WHITE=0;
int father[MAXN],vis[MAXN],dis[MAXN],color[MAXN];
int parent(int u)
{
    while(u!=father[u])
    {
        father[u]=father[father[u]];
        u=father[u];
    }
    return u;
}
void connect(int u,int v)//v作为u的儿子
{
    int fu=parent(u);
    int fv=parent(v);
    father[fv]=fu;
}
struct Node
{
    int to,next,val;
};
Node edge[MAXN<<1];
int head[MAXN],cnt;
void addEdge(int u,int v,int val)
{
    edge[cnt].to=v;
    edge[cnt].val=val;
    edge[cnt].next=head[u];
    head[u]=cnt++;
    edge[cnt].to=u;
    edge[cnt].val=val;
    edge[cnt].next=head[v];
    head[v]=cnt++;
}
struct Ask
{
    int u,to,next,lca;
};
Ask arr[70010*6];
int ask_head[MAXN],idx;
void addAsk(int u,int v)
{
    arr[idx].u=u;arr[idx].to=v;arr[idx].next=ask_head[u];ask_head[u]=idx++;
    arr[idx].u=v;arr[idx].to=u;arr[idx].next=ask_head[v];ask_head[v]=idx++;
}
void tarjan(int u)
{
    vis[u]=1;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        if(vis[v]==0)
        {
            dis[v]=dis[u]+edge[i].val;
            tarjan(v);
            connect(u,v);
        }
    }
    color[u]=BLACK;
    for(int i=ask_head[u];i!=-1;i=arr[i].next)
    {
        int v=arr[i].to;
        if(color[v]==BLACK)
        {
            int fa=parent(v);
            arr[i].lca=fa;
            arr[i^1].lca=fa;
        }
    }
}
int main()
{
    int t,n,m,u,v,w,val,cas=0;
    while(scanf("%d",&n)!=EOF)
    {
        if(cas++) printf("\n");
        memset(head,-1,sizeof(head));
        memset(ask_head,-1,sizeof(ask_head));
        memset(vis,0,sizeof(vis));
        memset(color,WHITE,sizeof(color));
        for(int i=1;i<=n;i++) father[i]=i;
        cnt=idx=0;
        for(int i=1;i

方法二:在线算法: RMQ算法

#include
#include
#include
using namespace std;
const int MAXN=50010;
const int MAXE=MAXN*2;
struct Node
{
    int to,next,val;
};
Node edge[MAXE];
int head[MAXN],cnt;
void addEdge(int u,int v,int val)
{
    edge[cnt].to=v;
    edge[cnt].val=val;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}
int first[MAXN];
int vis[MAXN];
int deep[MAXN*2];
int sequence[MAXN*20];
int dis[MAXN];
int dp[MAXN*2][25];
int tot;
void DFS(int u,int dep)
{
    vis[u]=1;
    sequence[++tot]=u;deep[tot]=dep;first[u]=tot;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        if(vis[v]==0)
        {
            dis[v]=dis[u]+edge[i].val;
            DFS(v,dep+1);
            sequence[++tot]=u;
            deep[tot]=dep;
        }
    }
}
void ST(int len)
{
    for(int i=1;i<=len;i++) dp[i][0]=i;
    for(int j=1;(1<y) swap(x,y);
    return sequence[RMQ(x,y)];
}
int main()
{
    int n,m,u,v,val,a,b,c;
    bool start=true;
    while(scanf("%d",&n)!=EOF)
    {
        if(!start) printf("\n");
        memset(head,-1,sizeof(head));
        memset(vis,0,sizeof(vis));
        cnt=tot=0;
        for(int i=1;i

题目推荐:
CODEVS 2370 小机房的树
CODEVS 1036 商务旅行
METO CODE 223 拉力赛
HDU 2586 How far way?
ZOJ 3195 Design the city

你可能感兴趣的:(LCA_最近公共祖先)