[Graph]Doubling Algorithm

Doubling Algorithm——倍增算法

是一种可以优化时间复杂度的神奇的算法,可以用它来求LCA等等。

基本思想:

deep[i] 表示 i节点的深度, fa[i,j]表示 i 的 2^j (即2的j次方) 倍祖先,那么fa[i , 0]即为节点i 的父亲,

然后就有一个递推式子:fa[i,j]= fa [ fa [i,j-1] , j-1] 。

那么怎么求LCA呢,我们需先把深度更深的节点往上提直到深度和另一个节点一样,然后我们每次调2^i个节点(从最大可以

跳的步数开始跳,若相反可能会导致找到的节点不是最近的),逐步缩小范围。

Luogu 3379:

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<iostream>
#define maxn 1000005
#define maxm 500005
using namespace std;
int n,p,gen;
struct Tree{
    int next,to;
}tree[maxn];
int head[maxm]={0},f[maxm][21]={0},deep[maxm]={0},tot=0;
void add(int x,int y){
    tot++;
    tree[tot].to=y;
    tree[tot].next=head[x];
    head[x]=tot;
}
void dfs(int u,int fa){
deep[u]=deep[fa]+1;
    //printf("u:%d deep:%d
",u,deep[u]);
    f[u][0]=fa;
    int i;
    for( i=1;(1<<i)<=deep[u];i++){
        //if(f[u][i-1])
        f[u][i]=f[f[u][i-1]][i-1];
        //else break;
    }
    for(i=head[u];i!=-1;i=tree[i].next){
        if(tree[i].to!=fa)
        dfs(tree[i].to,u);
    }
} 
int lca(int x,int y){
    if(deep[x]<deep[y])
    swap(x,y);
    for(int i=20;i>=0;i--){
        if(deep[y]<=deep[x]-(1<<i))
        x=f[x][i];
    }
    if(x==y) return x;
    for(int i=20;i>=0;i--){
        if(f[x][i]!=f[y][i]){
            x=f[x][i];
            y=f[y][i];
        }
    }
//    printf("x:%dy:%d
",x,y);
    return f[y][0];
}
int main()
{
    memset(head,-1,sizeof(head));
    int x,y,i;
    scanf("%d%d%d",&n,&p,&gen);
    for(i=1;i<n;i++){
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }
    dfs(gen,0);
    for(i=1;i<=p;i++){
        scanf("%d%d",&x,&y);
        printf("%d
",lca(x,y));
    }
    return 0;
}
倍增求LCA

 

原文地址:https://www.cnblogs.com/Fish-/p/8228468.html