HDU 2874 ST表求树上距离 并查集

思路:

  1. 先通过并查集判断两个点是否在同一棵树上
  2. 如果在同一棵树上,通过ST表查询树上两点距离

AC代码:

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e4+500;

int n,m,q,tot,beg[maxn],par[maxn],fa[maxn],dep[maxn],dis[maxn];
struct node{int to,nex,val;}edge[maxn*2];
int st[maxn][21];

inline void add(int u,int v,int val){
    edge[++tot].to = v;
    edge[tot].val = val;
    edge[tot].nex = beg[u];
    beg[u] = tot;
}

int find(int x){
    if(par[x]==x) return x;
    return par[x] = find(par[x]);
}

void dfs(int now,int f,int deep){
    dep[now] = deep , fa[now] = f;
    for(int i=beg[now];i;i=edge[i].nex){
        int nx = edge[i].to , val = edge[i].val;
        if(nx==f) continue;
        dis[nx] = dis[now] + val;
        dfs(nx,now,deep+1);
    }
}

void build(){
    for(int j=0;j<21;j++)
        for(int i=1;i<=n;i++)
            st[i][j] = j==0 ? fa[i]:0;
    for(int j=1;(1<<j)<=n;j++)
        for(int i=1;i<=n;i++)
            st[i][j] = st[st[i][j-1]][j-1];
}

int lca(int a,int b){
    if(dep[a]<dep[b]) swap(a,b);
    int i,j;
    for(i=0;(1<<i)<=dep[a];i++) ;	//得到可以跳的范围
    i--;
    for(j=i;j>=0;j--)
        if(dep[a]-(1<<j)>=dep[b])	//跳到和b的深度一样
            a = st[a][j];
    if(a==b) return a;
    for(j=i;j>=0;j--)
        if(st[a][j]&&st[a][j]!=st[b][j])	//一起向上跳
            a = st[a][j] , b = st[b][j];
    return fa[a];
}

int main(){
    while(scanf("%d%d%d",&n,&m,&q)!=EOF){
        for(int i=1;i<=n;i++) 	//初始化
        	par[i]=i,dep[i]=0,fa[i]=0,dis[i]=0,beg[i]=0;
        tot=0;
        for(int i=1,u,v,val;i<=m;i++){
            scanf("%d%d%d",&u,&v,&val);
            if(find(u)!=find(v)) par[find(u)] = find(v);
            add(u,v,val) , add(v,u,val);
        }
        for(int i=1;i<=n;i++){
            if(find(i)==i){
                dis[i] = 0;
                dfs(i,0,1);		//搜索每一棵子树
            }
        }
        build();
        while(q--){
            int a,b;
            scanf("%d%d",&a,&b);
            if(find(a)!=find(b)){	//判断是否在同一棵树上
                printf("Not connected\n");
            }else{					//查询dis
                printf("%d\n",dis[a]+dis[b]-2*dis[lca(a,b)]);
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值