hdu 2586 How far away ? LCA 最近公共祖先

本文介绍了一种解决多对点距离查询问题的方法,利用最近公共祖先的概念和树状数组进行优化,实现高效的距离计算。

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

题意: 给T组测试,每组n 个点,n-1 条边;求 m 对点的距离;

思路: 如果不懂 最近公共祖先这个概念,可以先去做 poj 1330 ,如果只是一对点(u,v),先一遍bfs 记录 每个节点的父亲节点;然后 从 u,v 往根节点走,

v = father[v]; u=father[u] ;知道第一次 u=v 此时 u (或者 v)  就是最近公共祖先; 时间复杂度 O( dep[u] + dep[v] )

          当要查询多对(u,v) 时,单个单个的一一查询显然是超时;用一 parent[step][v] 去记录 节点v 往根节点走step 步之后 可以达到的节点 v' 的 父亲节点;

         father [step + 1] = faher [step ] [ father[step][v] ]   当然 当达到根节点之后再继续走依旧是 -1 ;

反思:第一遍running error ,书本上是用dfs 写题想都没想 4000 个点,如果1 ……n 是一条直线,显然用递归爆栈;

另外题目表述又问题,他说要输出一空行,结果数据里面没有;


#include<stdio.h>
#include<string.h>
#include<math.h>
#include<string>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<list>
#include<map>
#include<set>
using namespace std;
const int N = 40010;
const int lgN = 16;

int n, m;
struct P{
    int to,val;
    P(int to = 0, int val = 0):to(to), val(val) {}
};

vector <P> e[N];
int parent[lgN][N];
int dep[N];
int dist[N];
int Max_step;
struct Que{
    int id,f;
    Que(int id = 0,int f = 0):id(id), f(f) {}

};
void deal_init(){
    for(int i = 1; i <= n; i++) e[i].clear();

    int a,b,c;
    for(int i = 1; i < n; i++){
        scanf("%d%d%d",&a,&b,&c);
        e[a].push_back(P(b,c));
        e[b].push_back(P(a,c));
    }
}

queue <Que> que;

void bfs(){
    que.push(Que(1,-1));
    dist[1] = 0;
    dep[1] = 0;
    while(!que.empty()){
        Que cur = que.front();que.pop();
        int i = cur.id;
        parent[0][i] = cur.f;
        vector <P> :: iterator it;
        for(it = e[i].begin(); it != e[i].end(); it++){
            if(it->to != cur.f){
                dist[it -> to] = dist[i] + (it -> val);
                dep[it -> to] = dep[i] + 1;
                que.push(Que((it -> to), i ));
            }
        }
    }

}
//void dfs(int i,int f,int Dep){
//    parent[0][i] = f;
//    dep[i] = Dep;
//    vector <P> :: iterator it;
//    for(it = e[i].begin(); it != e[i].end(); it++){
//        int next = it -> to;
//        if( next != f){
//            dist[next] = dist[i] + it -> val;
//            dfs(next, i, Dep+1);
//        }
//
//    }
//}
void deal_pre(){
//    dist[1] = 0;
//    dfs(1,-1,0);
    bfs();
    for(int step = 0; step < Max_step - 1; step++){
        for(int i = 1 ;i <= n; i++){
            if(parent[step][i] < 0) parent[step + 1][i] = -1;
            else parent[step + 1][i] = parent[step][ parent[step][i] ];
        }
    }
}
int LCA(int u,int v){
    if(dep[u] > dep[v]) swap(u,v);
    for(int step = 0; step < Max_step; step++){
        int dis = dep[v] - dep[u];
        if( dis >> step & 1) v = parent[step][v];
    }

    if(u == v) return u;
    for(int step = Max_step - 1; step >= 0; step--){
        if(parent[step][u] != parent[step][v]){
            u = parent[step][u];
            v = parent[step][v];
        }
    }

    return parent[0][u];
}
void work(){
    int a,b;
    while(m--){
        scanf("%d%d",&a,&b);
        int f = LCA(a,b);
        long long ans = abs(dist[a] - dist[f]) + abs(dist[b] - dist[f]);
        cout << ans << endl;
    }
//    printf("\n");
}
int main()
{

//	freopen("in.in","r",stdin);
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        Max_step = lgN;
        deal_init();
        deal_pre();
        work();
    }


	return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值