7_6_A题 How far away?题解[hdu 2586](LCA Tarjan算法)

题目链接

题意

一个村子中的房子被一些长度不等的道路链接,保证无环(即构成一颗树),给出一些询问,询问两个房子之间的距离是多少。

思路

很明显的求最近公共祖先,使用Tarjan算法,在求LCA的过程中,记录节点到根的距离dis,然后对于每个询问,则 ans=dis[u]+dis[v]2dis[lca]

代码

#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;

const int maxn = 4e4+5;
const int maxq = 205;

struct Edge{
    int to,cost;
    Edge(int t,int c):to(t),cost(c){};
};

struct Que{
    int to,lca,idx;
    Que(int t,int i):to(t),idx(i){};
};

vector <Edge> G[maxn];
vector <Que> Ques[maxn];
bool vis[maxn];
int dis[maxn];
int ans[maxq];
int fa[maxn];

void init(int n){
    for(int i = 0 ; i <= n ; i ++){
        G[i].clear();
        Ques[i].clear();
        fa[i] = i;
    }
    memset(dis,0,sizeof dis);
    memset(ans,0,sizeof ans);
    memset(vis,0,sizeof vis);
}

void addedge(int from, int to, int cost){
    G[from].push_back(Edge(to,cost));
    G[to].push_back(Edge(from,cost));
}

void addQue(int from, int to, int idx){
    Ques[from].push_back(Que(to,idx));
    Ques[to].push_back(Que(from,idx));
}

int Find(int x){
    return fa[x] == x ? fa[x]:fa[x] = Find(fa[x]);
}

void Union(int x, int y){
    int a = Find(x);
    int b = Find(y);
    if(a!=b) fa[b] = a;
}

void Tarjan(int cur){
    vis[cur] = true;
    for(int i = 0 ; i < G[cur].size(); i ++){
        Edge& e = G[cur][i];
        if(!vis[e.to]){
            dis[e.to] = dis[cur] + e.cost;
            Tarjan(e.to);
            Union(cur,e.to);
        }
    }
    for(int i = 0 ; i < Ques[cur].size() ; i ++){
        Que & q = Ques[cur][i];
        if(vis[q.to]){
            q.lca = Find(q.to);
            ans[q.idx] = dis[cur] + dis[q.to] - 2*dis[q.lca];
        }
    }
}

int main(){
    int T;
    scanf("%d", &T);
    while(T --){
        int n,q;
        scanf("%d %d",&n,&q);
        init(n);
        for(int i = 1 ; i < n ; i ++){
            int u,v,cost;
            scanf("%d %d %d",&u,&v,&cost);
            addedge(u,v,cost);
        }
        for(int i = 0 ; i < q ; i ++){
            int u,v;
            scanf("%d %d",&u,&v);
            addQue(u,v,i);
        }
        Tarjan(1);
        for(int i = 0 ; i < q; i ++){
            printf("%d\n",ans[i]);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值