首页 > 其他 > 详细

关于倍增方法的LCA的讲解

时间:2020-05-22 21:16:04      阅读:53      评论:0      收藏:0      [点我收藏+]

      在理解倍增方法前,先试着去搞懂逐步移动的LCA。逐步移动的方法,其实就是把深度较深的节点向着父节点方向移动,直至与另外一个节点深度相同。再将两节点同时向父节点方向移动,直至两节点相遇为止。预处理过程就是一个DFS,求出每个节点的深度。对于每一次查询,用逐步移动的方法求得LCA。如果节点数为N,树的高度为L,则预处理时间O(N),每一次查询的总时间最坏情况为O(L)。最坏情况下树排成一条链,即L=N,则在有Q次查询时查询总时间为O(QN)。通常N比较大,虽然它通常不能做到100%正确,但是在非极端数据下表现良好。

#include<iostream>
using namespace std;
struct TREE
{
    int pre;
    int to;
}tree[500010];
int edge_sum,head[500010];
int N,M,S,parent[500010],depth[500010];
void add_edge(int from,int to)
{
    edge_sum++;
    tree[edge_sum].pre=head[from];
    tree[edge_sum].to=to;
    head[from]=edge_sum; 
}
void dfs(int dep,int cur,int fa)
{
    depth[cur]=dep;
    parent[cur]=fa;
    for(int i=head[cur];i!=0;i=tree[i].pre)
    {
        int u=tree[i].to;
        if(u!=fa)dfs(dep+1,u,cur);
    }
}
int LCA(int u,int v)
{
    if(depth[u]<depth[v])swap(u,v);
    while(depth[u]>depth[v])u=parent[u];
    if(u==v)return u;
    while(u!=v)u=parent[u],v=parent[v];
    return u;
}
int main()
{
    cin>>N>>M>>S;
    for(int i=1;i<=N-1;i++)
    {
        int u,v;
        cin>>u>>v;
        add_edge(u,v);
        add_edge(v,u);
    }
    dfs(1,S,-1);
    for(int i=1;i<=M;i++)
    {
        int u,v;
        cin>>u>>v;
        cout<<LCA(u,v)<<endl;
    }
    return 0;
}

 

      搞明白了逐步移动的LCA,就可以做倍增方法的了。倍增方法的成立依赖于这个定理:任意一个正整数都可以表示成若干个不同的2的幂次之和。这其实不难理解,因为任意一个正整数肯定都可以用二进制来表示。定义数组parent[MAXN][MAX_LOG],其中parent[k][v]表示k的2v倍祖先。如果节点不存在就置为0。大致思路和逐步移动相似,只是用二次幂的移动来代替一步一步的移动。设有Q次询问和N个节点,预处理O(Nlog2N), 查询总时间O(Qlog2N),整个程序的时间复杂度总和为O((N+Q)log2N),可以通过全部的数据。

#include<cmath>
#include<stack>
#include<cstdio>
#include<string>
#include<iostream>
using namespace std;
struct TREE
{
    int pre;
    int to;
}tree[1000010];
int edge_sum,head[500010];
int N,M,S,U,V,parent[500010][20],depth[500010];
void add_edge(int from,int to)
{
    edge_sum++;
    tree[edge_sum].pre=head[from];
    tree[edge_sum].to=to;
    head[from]=edge_sum; 
}
void dfs(int dep,int cur,int fa)
{
    depth[cur]=dep;
    parent[cur][0]=fa;
    for(int i=head[cur];i!=0;i=tree[i].pre)
    {
        int u=tree[i].to;
        if(u!=fa)dfs(dep+1,u,cur);
    }
}
int query_LCA(int u,int v)
{
    if(u==S||v==S)return S;
    if(depth[u]<depth[v])swap(u,v);
    int num=depth[u]-depth[v];
    for(int k=0;(1<<k)<=num;k++)
        if((1<<k)&num)u=parent[u][k];
    if(u==v)return u;
    for(int k=log(N)/log(2)-1;k>=0;k--)
        if(parent[u][k]!=parent[v][k])
            u=parent[u][k],v=parent[v][k];
    return parent[u][0];
}
int main()
{
    scanf("%d %d %d",&N,&M,&S);
    for(int i=1;i<=N-1;i++)
    {
        scanf("%d %d",&U,&V);
        add_edge(U,V);
        add_edge(V,U);
    }
    dfs(1,S,S);
    for(int k=1;(1<<k)<=N;k++)
        for(int v=1;v<=N;v++)
            parent[v][k]=parent[parent[v][k-1]][k-1];
    for(int i=1;i<=M;i++)
    {
        scanf("%d %d",&U,&V);
        printf("%d\n",query_LCA(U,V));
    }
    return 0;
}

 

关于倍增方法的LCA的讲解

原文:https://www.cnblogs.com/Mr-Kyle/p/12939302.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!