倍增法求最近公共祖先 lca

来源:互联网 发布:cmd查找他机mac 编辑:程序博客网 时间:2024/05/16 04:57

lca:

在树上的两节点都向上走最先找到的公共祖先。

倍增:

fa[x][i]表示x节点的第2^i个祖先(fa[x][0]就表示父节点),deep[x]表示节点的深度。

大致思路:

1.dfs建树,同时确定每个节点的父节点,更新deep数组,x节点的deep值为其父节点的deep值+1。

2.由fa数组定义可得,fa[x][j]=fa[fa[x][j-1]][j-1],即第2^(j-1)个祖先的第2^(j-1)个祖先,因为2^(j-1)+2^(j-1)=2^j。所以我们用此性质准备好fa数组以及deep数组(prepare函数)。

3.lca函数,对于查询的两个点x,y。如果x==y直接返回x。继续,令x为deep值大的点。说不清具体看代码解释。

代码:

#include<stdio.h>#include<string.h>#include<algorithm>#include<iostream>using namespace std;int scan(){    int x=0;    char c=getchar();    while(c>'9'||c<'0')        c=getchar();    while(c>='0'&&c<='9')        x=(x<<1)+(x<<3)+c-'0',        c=getchar();    return x;}struct node{    int to,next,dis;};node edge[101000];int edge_num,pre[101000];int fa[101000][20],Log,deep[101000];int n,m;void add(int x,int y){    edge[++edge_num].next=pre[x];    edge[edge_num].to=y;    pre[x]=edge_num;    return;}void dfs(int x){    int i;    for(i=pre[x];i;i=edge[i].next){        int v=edge[i].to;        if(v==fa[x][0])continue;  //注意        deep[v]=deep[x]+1;   //准备deep数组        fa[v][0]=x;        dfs(v);    }    return;}void prepare(){    for(int j=1;j<=Log;j++)   //准备fa数组        for(int i=1;i<=n;i++)            fa[i][j]=fa[fa[i][j-1]][j-1];    return;}int lca(int x,int y){    if(x==y)return x;   //如果x==y,返回x    if(deep[x]<deep[y]) //令x为deep大的点        swap(x,y);    for(int i=Log;i>=0;i--)   //令x与y的深度相同        if(deep[fa[x][i]]>=deep[y]&&fa[x][i])  //防止y为根            x=fa[x][i];  //往上跳//倍增优势在于跳得快,且不会跳过    if(x==y)  //如果x==y,返回x        return x;    for(int i=Log;i>=0;i--){  //令x与y同时向上跳(相同步数)        if(fa[x][i]!=fa[y][i]){            x=fa[x][i];y=fa[y][i];        }    }    return fa[x][0];  //注意,返回x的父节点}int main(){    n=scan();m=scan();    int i;    for(Log=0;(1<<Log)<=n;Log++);  //Log为树的最大深度    Log--;    for(i=1;i<=n-1;i++){        int x=scan(),y=scan();        add(x,y);        add(y,x);     }    dfs(1);    prepare();    for(i=1;i<=m;i++){   //m个询问        int x=scan(),y=scan();        printf("%d\n",lca(x,y));    }}
原创粉丝点击