LibreOJ 10130 点的距离(倍增法求LCA模板)

题目链接https://loj.ac/problem/10130
在这里插入图片描述
分析
树上两点(x,y)之间的距离为:x的深度+y的深度-LCA(x,y)的深度
理论
LCA:最近公共祖先
暴力
(1)先DFS一遍找出每个点的DEP(深度)。
(2)深度大的顶点往深度小的顶点跳,跳到深度相同。
(3)如果不是同一个点,两个点继续向上跳。
优化(树上倍增法)
f[x,k]表示x的2^k辈祖先,即从x向根节点走 2^k步到达的结点。如果结点不存在,则令f[x,k]=0.
f[x,0]就是x的父亲结点。
x向根节点走2^k步《=》向根走 2^(k-1),再走 2^(k-1)。所以对于k属于[1,logn],有f[x][k]=f[f[x][k-1]][k-1]。
将f数组预处理出来,在计算LCA
1.设dep[x]表示x的深度。不妨设dep[x]>=dep[y],否则交换x和y。
2.用二进制拆分的思想,将x向上调整到和y相同的深度。

代码

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int fir[N],net[2*N],to[2*N],num;
int f[N][21],dep[N];
void Index(int x,int y)
{
    to[++num]=y;
    net[num]=fir[x];
    fir[x]=num;
}
void Deal_first(int u,int father)/*预处理出每个结点的深度以及每个结点走2^0 2^1 2^2...步到达的结点*/
{
    dep[u]=dep[father]+1;
    for(int i=0; i<=19; i++)
        f[u][i+1]=f[f[u][i]][i];
    for(int i=fir[u]; i; i=net[i])
    {
        int v=to[i];
        if(v==father)/*因为无向边,所以会出现v=father的情况*/
            continue;
        f[v][0]=u;
        Deal_first(v,u);
    }
}
int LCA(int x,int y)
{
    if(dep[x]<dep[y])
        swap(x,y);
    for(int i=20; i>=0; i--)
    {
        if(dep[f[x][i]]>=dep[y])
            x=f[x][i];
        if(x==y) return x;

    }
    for(int i=20; i>=0; i--)
    {
        if(f[x][i]!=f[y][i])/*刚开始可能跳到结点不存在的位置,f[x][i]=f[y][i]=0,不执行if选择语句*/
        {
            x=f[x][i];
            y=f[y][i];
        }
    }
    return f[x][0];
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1,x,y; i<=n-1; i++)
    {
        scanf("%d%d",&x,&y);
        Index(x,y);
        Index(y,x);
    }
    Deal_first(1,0);
    int q;
    scanf("%d",&q);
    while(q--)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        printf("%d\n",dep[x]+dep[y]-2*dep[LCA(x,y)]);
    }
    return 0;
}
发布了165 篇原创文章 · 获赞 6 · 访问量 5067

猜你喜欢

转载自blog.csdn.net/lylzsx20172018/article/details/103301805