最近公共祖先模板

洛谷3379:https://www.luogu.org/problemnew/show/P3379

稍微谈下理解,首先建边的时候建两条因为开始无法判断哪些是父节点哪些是子节点,首先dfs1处理每一个点的深度,和每一个点2^k之前的祖先的值,具体维护方法类似动态规划,就是你爷爷是你爸爸的爸爸,即dp[son][i+1]=dp[dp[son][i]][i];(因为2^i+1辈祖先是2^i辈祖先的2^i辈祖先)然后根据建的边一路往下走。那么在正式处理lca的过程中是这样操作的,首先使倍增使x和y深度相同,这里一定要从大到小枚举,因为例如8的二进制为1000,但你从末位开始模拟你是不知道要不要取1,2,4,的,这里等于是用了一个二进制唯一性加一点点贪心的想法(其实没那么复杂,和正常二进制转换那个原理一样)。两个深度跳到一样了,这时候仍然从大到小枚举,找的是最后一个不是两者公共祖先的点,也就是说这个点一定是最近公共祖先的子节点,最后返回dp[x][0]。附上代码:

#include<math.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int dp[500005][25]={0},nxt[1000005]={0},head[1000005]={0},to[1000005]={0},dep[500005]={0};
int n,m,s,tmp=0;
int add(int x,int y)
{
    tmp++;
    to[tmp]=y;
    nxt[tmp]=head[x];
    head[x]=tmp;
    return 0;
}
int dfs1(int son,int father)
{
    int i,t;
    dep[son]=dep[father]+1;
    for(i=0;pow(2,i)*2<=dep[son];i++)
      dp[son][i+1]=dp[dp[son][i]][i];
    for(i=head[son];i;i=nxt[i])
      {
      t=to[i];
      if(t==father)  continue;
      dp[t][0]=son;
      dfs1(t,son);
      }
    return 0;
}
int lca(int x,int y)
{
    int t,i;
    if(dep[x]<dep[y])
      {
      t=x;x=y;y=t;
      }
    for(i=20;i>=0;i--)
      {
      if(dep[dp[x][i]]>=dep[y])  x=dp[x][i];
      if(x==y)  return x;
      }
    for(i=20;i>=0;i--)
      {
      if(dp[x][i]!=dp[y][i])
        {
        x=dp[x][i];
        y=dp[y][i];
        }
      }
    return dp[x][0];
}
int main()
{
    int i,x,y;
    scanf("%d%d%d",&n,&m,&s);
    for(i=1;i<=n-1;i++)
      {
      scanf("%d%d",&x,&y);
      add(x,y);
      add(y,x);
      }
    dfs1(s,0);
    for(i=1;i<=m;i++)
      {
      scanf("%d%d",&x,&y);
      printf("%d\n",lca(x,y));
      }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40892508/article/details/82766292