[OI学习笔记]倍增LCA

这也是洛谷P3379 【模板】最近公共祖先(LCA)的代码

1.首先预处理upto[i][j]表示点i向上跳2j 个点到达的点,d[i]表示i的深度

2.然后把两个被询问的点搬到同一深度,具体操作是:

  假设深的点为a,那么a每次把a迭代为 upto[a][log2(d[a]-d[b])] ,直到d[a]==d[b]

3.将他们不断向上跳尽量大,使他们不重合,直到不能再在这个条件下向上跳

  具体操作是从大到小枚举k(最大 k=log2(深度) ,因为最多跳到root),然后a,b都向上跳2k 个点,直到不能再跳

4.不能跳之后,那么ans就是他们的父亲了

(p.s. 还可以实现预处理数组lg[i]=log2(i)+1以加快速度,具体实现看代码)

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
const int MAXN = 500001;

inline int read(){
    int x=0,w=1;char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-')w=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        x=(x<<3)+(x<<1)+c-'0';
        c=getchar();
    }
    return x*w;
}

int n,t,root;
int upto[MAXN][25],d[MAXN]={0},first[MAXN],lg[MAXN];

struct edge{
    int u,v,next;
}e[2*MAXN];

int tot=0;
void insert(int u,int v){
    ++tot;e[tot].u=u;e[tot].v=v;e[tot].next=first[u];first[u]=tot;
}

void init(int u,int fa){
    d[u]=d[fa]+1;
    upto[u][0]=fa;
    for(int k=1;(1<<k)<=d[u];k++){
        upto[u][k]=upto[upto[u][k-1]][k-1];
    }
    for(int i=first[u];i!=-1;i=e[i].next){
        int v=e[i].v;
        if(v!=fa)init(v,u);
    }
}

int solve(int a,int b){
    if(d[a]<d[b])swap(a,b);
    while(d[a]>d[b]){
        a=upto[a][lg[d[a]-d[b]]-1]; 
    }
    if(a==b)return a;
    for(int k=lg[d[a]]-1;k>=0;k--)
        if(upto[a][k]!=upto[b][k]){
            a=upto[a][k];b=upto[b][k];
        }
    return upto[a][0];
}

int main(){
    memset(first,-1,sizeof(first)); 
    n=read();t=read();root=read();
    for(int i=1;i<n;i++){
        int x=read(),y=read();
        insert(x,y);
        insert(y,x);
    }
    init(root,0);
    for(int i=1;i<=n;i++)
        lg[i]=lg[i-1]+(1<<lg[i-1]==i);//预处理lg[i]=log2(i)+1 
    for(int i=1;i<=t;i++){
        int a=read(),b=read();
        printf("%d\n",solve(a,b));
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/sjrb/p/10390627.html
今日推荐