LCA(最近公共祖先)

定义
对于有根树T的两个结点u、v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u、v的祖先且x的深度尽可能大。另一种理解方式是把T理解为一个无向无环图,而LCA(T,u,v)即u到v的最短路上深度最小的点。现在给定一个根为1的树,求某两个点的最近公共祖先。
思路

预处理出每个点的深度再一层一层返回一直到找到相同点

deep[1]=1;
void dfs(int nw,int d){
    deep[nw]=d;
    for(int i=0;i<G[nw].size();i++){
        int to=G[nw][i];
        if(!deep[to]){
            par[to]=nw;
            dfs(to,d+1);
        }
    }
}

再返回

int LCA(int a,int b){
    if(deep[a]>deep[b])swap(a,b);
    while(deep[a]<deep[b])b=par[b];
    while(a!=b){
        a=par[a];
        b=par[b];
    }return a;
}

复杂度约为O(n)
但是可以使用倍增优化
以par[i][j] 记录第i个点向上返回2^j的节点
因为2^j==2*2^(j-1)
即得到par[i][j]=ar[par[i][j-1]][j-1];
于是每次dfs出每个点的par[i][0]

void dfs(int c){
    for(int i=0;i<G[c].size();i++){
        int t=G[c][i];
        if(!d[t]){
            d[t]=d[c]+1;
            par[t][0]=c;
            dfs(t);
        }   
    }   
}

再利用动态规划计算出par[i][j]

for(int j=1;j<S;j++)
    for(int i=1;i<=n;i++)
    par[i][j]=par[par[i][j-1]][j-1];

于是我们可以利用类似快速幂的思想

int up(int a,int s){
    for(int i=0;i<S;i++)
        if(s&(1<<i))a=par[a][i];
    return a;
}

于是我们就得到了o(log2n)的算法

int LCA(int a,int b){
    if(d[a]>d[b])swap(a,b);
    b=up(b,d[b]-d[a]);
    if(a!=b){
    for(int i=S-1;i>=0;i--)
        if(par[a][i]!=par[b][i])
        a=par[a][i],b=par[b][i];
        //找到最近的不同点 
    a=par[a][0];
    //再上一层即为LCA 
    }return a;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值