LCA两种方法

来源:互联网 发布:linux创建文件夹 命令 编辑:程序博客网 时间:2024/06/06 06:54

LCA

LCA(Least Common Ancestors),即最近公共祖先,是指在有根树中,找出某两个结点u和v最近的公共祖先。

模板题

倍增法(在线)

anc[i][j]表示第i个点的2^j的祖先的标号整个过程就是两个点往上跳到同一深度,再一起往上跳找到LCA
#include<cstdio>#include<cmath>using namespace std;int n,m,root,anc[500009][21];int edge_num,head[1000009],deep[500009];struct E{    int to,next;}edge[1000009];inline void Swap(int &a,int &b){    int t=a;    a=b;b=t;}inline int Read(){    int x=0,flag=1;char ch=getchar();    while(ch<'0'||ch>'9'){if(ch=='-') flag=-1;ch=getchar();}    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x*flag;}void addedge(int x,int y){    edge[++edge_num].next=head[x];    edge[edge_num].to=y;    head[x]=edge_num;}void DFS(int x,int fa){    int i;    for(i=head[x];i;i=edge[i].next){        if(edge[i].to==fa) continue;//important        deep[edge[i].to]=deep[x]+1;        anc[edge[i].to][0]=x;        DFS(edge[i].to,x);    }}void PREANC(){    int i,j;    for(j=1;(1<<j)<=n;j++){        for(i=1;i<=n;i++){            if(anc[i][j-1]){//有祖先                anc[i][j]=anc[anc[i][j-1]][j-1];            }        }    }}int LCA(int x,int y){    if(deep[x]<deep[y]) Swap(x,y);    int i;    int maxlog=floor(log(n)/log(2));    for(i=maxlog;i>=0;i--){        if(deep[x]-(1<<i)>=deep[y])            x=anc[x][i];    }    if(x==y) return x;    for(i=maxlog;i>=0;i--){        if(anc[x][i]&&anc[x][i]!=anc[y][i]){            x=anc[x][i];y=anc[y][i];        }    }    return anc[x][0];}int main(){    n=Read();m=Read();root=Read();    int i;    for(i=1;i<=n-1;i++){        int x=Read(),y=Read();        addedge(x,y);addedge(y,x);    }    DFS(root,0);    PREANC();    for(i=1;i<=m;i++){        int x=Read(),y=Read();        printf("%d\n",LCA(x,y));    }    return 0;}

Tarjan(离线)

Tarjan是人名

    思想是DFS找到某条路最深的点,将其加入并查集,祖先为自己,然后回溯;将上一个点也加入并查集,共同祖先为上一个点。。。不断进行此操作    再DFS过程中不断加入新的点到并查集中,同时检查有没有一组问答已经在并查集内,如果有,输出一组答案
#include<cstdio>using namespace std;int n,m,root,edge_num,ask_num;int fa[500010],head[1200000],h1[1200000],ans[500010];bool b[500010];struct E{    int next,to;}edge[1200000];struct A{    int next,to,num;}ask[1200000];void addedge(int x,int y){    edge[++edge_num].next=head[x];    edge[edge_num].to=y;    head[x]=edge_num;}void addask(int x,int y,int z){    ask[++ask_num].next=h1[x];    ask[ask_num].to=y;    ask[ask_num].num=z;    h1[x]=ask_num;}int Find(int x){    if(fa[x]!=x) fa[x]=Find(fa[x]);    return fa[x];}/*void Union(int a,int b){    a=Find(a);b=Find(b);    fa[a]=b;}*/void LCA(int x){    b[x]=1;    fa[x]=x;    int j,u;    for(j=head[x];j;j=edge[j].next){        u=edge[j].to;        if(!b[u]){            LCA(u);            fa[u]=x;        }    }    int k;    for(j=h1[x];j;j=ask[j].next){        u=ask[j].to;        k=ask[j].num;        if(b[u]){            ans[k]=Find(u);        }    }}void out(){    for(int o=1;o<=m;o++){        printf("%d\n",ans[o]);    }}int main(){    scanf("%d%d%d",&n,&m,&root);    int i,ask1,ask2,a1,b1;    for(i=1;i<=n-1;i++){        scanf("%d%d",&a1,&b1);        addedge(a1,b1);addedge(b1,a1);    }    for(i=1;i<=m;i++){        scanf("%d%d",&ask1,&ask2);        addask(ask1,ask2,i);        addask(ask2,ask1,i);    }    LCA(root);    out();    return 0;}

相比之下,Tarjan比LCA要快几乎一倍,但倍增可以在两点到LCA的路上很容易地搞事情,比如NOIP2013 DAY1 T3
0 0
原创粉丝点击