倍增法在线求LCA(详解)

先贴上学习的文章:

洛谷P3379题解1

倍增讲解

在用Tarjan算法求LCA时会出现超时的现象,因为是一层一层的往上跳,于是就有了用倍增法求LCA,是2^{^{k}}层地往上跳。复杂度:O(nlogn)

1.倍增法思想:

按2的倍数跳,不过是从大到小跳,32,16,8,4,2,1。原因是从小到大跳会出现“悔棋”的现象:

从小到大:5!=1+2+4

从大到小:5=4+1

5<32 (不跳)

5<16(不跳)

5<8(不跳)

5>=4(向上跳4格到1)

1<2(不跳)

1>=1(向上跳1格到0)

2.算法解析:

step1:预处理

目标:

  1. 在dfs的时候求出每个节点的深度。
  2. 求出fa[x][i]:=x节点向上跳2^{^{i}}层后在哪个节点上,这里要注意,节点可能向上跳地太猛了,以至于都跳出了根节点,这种对我们做题没有意义,所以只要求出向上跳的最大层数(也就是节点深度dep[x])就可以啦~

可知:fa[x][0]=x的父节点

          fa[x][1]=fa[x的父节点][0]=fa[fa[x][0]][0]

          fa[x][i]=fa[fa[x][i-1]][i-1]

原因是:比如向上跳2^{^{3}}=8层:

当前i节点向上跳8/2=4=2^{^{2}}层,到达点j

再由j节点向上跳8/2=4=2^{^{2}}层,到达目的节点。(一共跳了2^{^{3}}层)

void dfs(int now,int pre){
    dep[now]=dep[pre]+1;
    vis[now]=1;
    fa[now][0]=pre;
    for(int i=1;(1<

step2:常数优化

节点n向上跳的范围是[1,log2(n)]

for(int i=1;i<=n;i++){//求出log2(i)+1的值
    lg[i]=lg[i-1]+(1<

可以模拟一下:

lg[1]=0+(1==1)=1

lg[2]=1+(1==1)=2

lg[3]=2+(4==3)=2

lg[4]=2+(4==4)=3

这里为什么用递推公式求log2(i)+1,而不用cmath里的函数求log2(i),原因是:

log是以e为底数, log10是以10为底数

求log2(i),则:log(i)/log(2)速度慢,不如求出log2(i)+1的值存在lg[i]数组里,需要的时候用lg[i]-1

step3:倍增LCA

有了上两步的铺垫,现在就好求多了。

求倍增LCA的思想是:

  1. 先把两个点提到统一高度,再一起跳。
  2. 向上跳也是有原则的:要跳到LCA的下面一层。然后该层对应节点的父节点就是要求的LCA

倍增法在线求LCA(详解)_第1张图片

比如节点7和9,9先跳到8,此时和7是同一层,若是跳地太多到1,就会错认为1是LCA

正确的是7->4   9->8->5

fa[4][0]或fa[5][0]=3就是答案


int lca(int x,int y){
    if(dep[x]dep[y]){
        x=fa[x][lg[dep[x]-dep[y]]-1];
    }
    if(x==y)return x;
    for(int k=lg[dep[x]];k>=0;k--){
        if(fa[x][k]!=fa[y][k]){
            x=fa[x][k];
            y=fa[y][k];
        }
    }
    return fa[x][0];
}

完结撒花✿✿ヽ(°▽°)ノ✿

上一道模板题:

hdu2874

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef pairP;
const int INF=0x3f3f3f3f;
const int N=100015;

int head[N],cnt,dis[N],pre[N];
int dep[N],fa[N][22],lg[N];
int vis[N];
struct A{
    int to,nex,cost;
}edge[N];

int f(int x){
    if(pre[x]==x)return pre[x];
    pre[x]=f(pre[x]);
    return pre[x];
}

void link(int a,int b){
    int r1=f(a),r2=f(b);
    if(r1!=r2){
        pre[r2]=r1;
    }
}

void add(int from,int to,int cost){
    edge[cnt].to=to;
    edge[cnt].cost=cost;
    edge[cnt].nex=head[from];
    head[from]=cnt++;
}

void dfs(int now,int pre){
    dep[now]=dep[pre]+1;
    vis[now]=1;
    fa[now][0]=pre;
    for(int i=1;(1<dep[y]){
        x=fa[x][lg[dep[x]-dep[y]]-1];
    }
    if(x==y)return x;
    for(int k=lg[dep[x]];k>=0;k--){
        if(fa[x][k]!=fa[y][k]){
            x=fa[x][k];
            y=fa[y][k];
        }
    }
    return fa[x][0];
}

void init(int n){
    for(int i=1;i<=n;i++)pre[i]=i;
    memset(head,-1,sizeof(head));
    memset(vis,0,sizeof(vis));
    memset(dep,0,sizeof(dep));
    memset(dis,0,sizeof(dis));
    cnt=0;
    memset(fa,0,sizeof(fa));
}

int main(){
    int n,m,k,s,t;
    for(int i=1;i<=1000;i++){
        lg[i]=lg[i-1]+(1<

 

你可能感兴趣的:(LCA)