思路:
- 先通过并查集判断两个点是否在同一棵树上
- 如果在同一棵树上,通过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;
}