点的距离 (倍增求LCA)

本文介绍了一种解决树上两点间距离查询问题的方法,通过预处理最近公共祖先(LCA)和节点深度,实现了快速查询任意两点间的距离。使用邻接表表示树结构,动态规划求解LCA,最终通过Dep[x]+Dep[y]-2*Dep[LCA(x,y)]公式计算距离。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

给定一棵 n个点的树,m 个询问,每次询问点 x到点 y两点之间的距离。

输入格式

第一行一个正整数 ,表示这棵树有 n 个节点;

接下来 n - 1 行,每行两个整数 x,y 表示 x,y 之间有一条连边;

然后一个整数 m,表示有 m 个询问;

接下来 m 行每行两个整数 x,y  表示询问 x 到 y 的距离。

输出格式

输出 一 行,每行表示每个询问的答案。

样例

样例输入

6
1 2
1 3
2 4
2 5
3 6
2
2 6
5 6

样例输出

3
4

数据范围与提示

对于全部数据,1<=n<=1e5     1<=x,y<=n。

思路:因为任意两点之间只有一条边,因此是此图是一棵树。那么此问题就转换成了LCA(最近公共祖先),任意两点之间的距离等于 Dep[x] + Dep[y] - 2 * Dep[LCA(x,y)] ;

上模板了:

 

#include<bits/stdc++.h>
#define N 100010
using namespace std;
int n,tot;
int Dep[N],f[N][22];  //f[i][j] 第i号节点往上走 2^j 层的节点是谁
int Next[N<<1],first[N<<1],go[N<<1];
void add(int u,int v) //邻接表建树
{
    Next[++tot]=first[u];
    first[u]=tot;
    go[tot]=v;
}
void Deal_first(int u,int father) //预处理
{
    Dep[u]=Dep[father]+1;  //u的父节点是father ,其深度为父节点的深度+1
    for(int i=0; i<=19; i++)  //因为 2^j = 2^(j-1) * 2^(j-1)
        f[u][i+1]=f[f[u][i]][i]; //动态归化找u往上走的所有层
    
    int e=first[u];  
    while(e)
    {
        int v=go[e];  //u -> v
        if(v==father) //因为是无向图(father -> u )所以直接跳过即可
        {
            e=Next[e];
            continue;
        }
        f[v][0]=u; // v 的 (2^(0) = 1) 就是 u
        Deal_first(v,u);  //递归处理
        e=Next[e];
    }
}
int LCA(int x,int y)  
{
    if(Dep[x]<Dep[y])  //规定x的深度大
        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;  //y 是 x 的祖宗
    }
    for(int i=20; i>=0; i--)  //此时x 和 y 拥有相同的深度
    {
        if(f[x][i]!=f[y][i])  //两个同时往”树根“走
        {
            x=f[x][i];
            y=f[y][i];
        }
    }
    return f[x][0];  //x,y是深度最浅且不同的点,及LCA的子节点,所以向上找一个
}
int dist(int u,int v)
{
    return Dep[u]+Dep[v]-2*Dep[LCA(u,v)];
}
int main()
{
    tot=0;
    scanf("%d",&n);
    for(int i=1; i<n; i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y);   //无向图
        add(y,x);
    }
    Deal_first(1,0); //预处理
    int q;
    scanf("%d",&q);
    while(q--)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        printf("%d\n",dist(x,y));
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值