[luogu3379]最近公共祖先(树上倍增求LCA)

题意:求最近公共祖先。

解题关键:三种方法,1、st表 2、倍增法 3、tarjan

此次使用倍增模板(最好采用第一种,第二种纯粹是习惯)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<iostream>
#include<cmath>
using namespace std;
typedef long long ll;
int n,m,root,cnt,u,v,head[500005],dep[500005],fa[500005][21];
struct edge{
    int nxt;
    int to;
}e[1000005];
void add_edge(int u,int v){//单向
    e[cnt].to=v;
    e[cnt].nxt=head[u];
    head[u]=cnt++;
}
void dfs(int u){
    for(int i=1;(1<<i)<=dep[u];i++){
        fa[u][i]=fa[fa[u][i-1]][i-1];
    }
    for(int i=head[u];~i;i=e[i].nxt){
        int v=e[i].to;
        if(v==fa[u][0]) continue;
        fa[v][0]=u;
        dep[v]=dep[u]+1;
        dfs(v);
    }
}
int lca(int u,int v){
    if(dep[u]<dep[v]) swap(u,v);
    int d=dep[u]-dep[v];
    for(int i=0;(1<<i)<=d;i++) if(d&(1<<i)) u=fa[u][i];//转化到两节点深度相同,类似于快速幂的思想
    if(u==v) return u;
    for(int i=20;i>=0;i--){
        if(fa[u][i]!=fa[v][i]){
            u=fa[u][i];
            v=fa[v][i];
        }
    }
    return fa[u][0];
}
int main(){
    memset(head,-1,sizeof head);
    scanf("%d%d%d",&n,&m,&root);
    for(int i=1;i<n;i++){
        scanf("%d%d",&u,&v);
        add_edge(u,v);
        add_edge(v,u);
    }
    dfs(root);
    while(m--){
        scanf("%d%d",&u,&v);
        printf("%d\n",lca(u,v));
    }
    return 0;
}

 2、熟悉的树dp方式

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<iostream>
#include<cmath>
using namespace std;
typedef long long ll;
int n,m,root,cnt,u,v,head[500005],dep[500005],par[500005][21];
struct edge{
    int nxt;
    int to;
}e[1000005];
void add_edge(int u,int v){//单向
    e[cnt].to=v;
    e[cnt].nxt=head[u];
    head[u]=cnt++;
}
void dfs(int u,int fa){
    for(int i=1;(1<<i)<=dep[u];i++){
        par[u][i]=par[par[u][i-1]][i-1];
    }
    for(int i=head[u];~i;i=e[i].nxt){
        int v=e[i].to;
        if(v==fa) continue;
        par[v][0]=u;
        dep[v]=dep[u]+1;
        dfs(v,u);
    }
}
int lca(int u,int v){
    if(dep[u]<dep[v]) swap(u,v);
    int d=dep[u]-dep[v];
    for(int i=0;(1<<i)<=d;i++) if(d&(1<<i)) u=par[u][i];//转化到两节点深度相同,类似于快速幂的思想
    if(u==v) return u;
    for(int i=20;i>=0;i--){
        if(par[u][i]!=par[v][i]){
            u=par[u][i];
            v=par[v][i];
        }
    }
    return par[u][0];
}
int main(){
    memset(head,-1,sizeof head);
    scanf("%d%d%d",&n,&m,&root);
    for(int i=1;i<n;i++){
        scanf("%d%d",&u,&v);
        add_edge(u,v);
        add_edge(v,u);
    }
    dfs(root,-1);
    while(m--){
        scanf("%d%d",&u,&v);
        printf("%d\n",lca(u,v));
    }
    return 0;
}

 

posted @ 2019-02-05 01:08  Elpsywk  阅读(114)  评论(0编辑  收藏  举报