GYM101808 K. Another Shortest Path Problem(LCA)

https://codeforces.com/gym/101808/problem/K

/* 这道题当板子去学了,挑战上的板子,加了个权值

在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先,就是两个节点在这棵树上深度最大的公共的祖先节点。
换句话说,就是两个点在这棵树上距离最近的公共祖先节点。
所以LCA主要是用来处理当两个点仅有唯一一条确定的最短路径时的路径。 */

 

题意:n个点n个边的带权无向图,q次查询,问你任意两点间的最短距离

题解:倍增LCA,这样想:

          1.把图拆一条边,被变成一棵树跑LCA,拆边后可以利用dfs预处理每个点到根节点1的距离dis,

          2.树上任意两点间的距离等于而树上任意两点a,b的距离 len=dis[a]+dis[b]-2*dis[LCA(a,b)]

   3.由于拆了一条边,在把那条边考虑是就可以了

  

	                int u,v;
			cin>>u>>v;
			int ans=get_dis(u,v);
			ans=min(ans,get_dis(xx,u)+get_dis(yy,v)+zz);//xx,yy,为拆的那条边
			ans=min(ans,get_dis(xx,v)+get_dis(yy,u)+zz);
			cout<<ans<<endl;

代码如下:

/*
对于一颗树,我们可以dfs预处理处每个节点 i 到1号节点的距离dis[i]
而树上任意两点a,b的距离len=dis[a]+dis[b]-2*dis[LCA(a,b)]
*/
#include<cstdio>
#include<cstring>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<stdlib.h>
#include<vector>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=1000000;
#define P pair<int,int>
int par[60][100005*2];//j这个点向上跳2^i个的结点 
int dep[100005*2];//深度 
long long  dis[100005*2];//该结点到根节点1的距离 
vector<P> G[100005*2];
int MAXK;//能跳到的步数  2^k 
int root;
void dfs(int v,int p,int d){
	par[0][v]=p;//v这个节点向上跳2^0个就是p 
	dep[v]=d;
	for(int i=0;i<G[v].size();i++){
		if(G[v][i].second!=p){
			dis[G[v][i].second]=dis[v]+G[v][i].first; //更新距离 
			dfs(G[v][i].second,v,d+1);
		} 
	}
} 
void init(int V){
	dfs(root,-1,0);
	for(int k=0;k+1<MAXK;k++){
		for(int v=1;v<=V;v++){
			if(par[k][v]==-1) par[k+1][v]=-1;
			else par[k+1][v]=par[k][par[k][v]];
		}
	}
}
int lca(int u,int v){
	if(dep[u]>dep[v])  //保证每次都是可以从最深的点往上跳 
	    swap(u,v);
	for(int k=0;k<MAXK;k++){
		if( (dep[v]-dep[u]) >> k & 1){//相当于二进制枚举是1的位置 
			v=par[k][v];
		}
	}
	if(u==v) 
	    return u;
	for(int k=MAXK-1;k>=0;k--){
		if(par[k][u]!=par[k][v]){//不是同一层 
			u=par[k][u];
			v=par[k][v]; 
		}
	} 
	return par[0][u];
}
long long get_dis(int u,int v){
	return dis[u]+dis[v]-2*dis[lca(u,v)];
}
int main(){
	int t;
	cin>>t;
	while(t--){
		root=1;
		MAXK=32;//最大跳跃的2^K 
		int N,Q;
		cin>>N>>Q;
		for(int i=1;i<=100005*2;i++)//注意 
		    G[i].clear();
		for(int i=0;i<N-1;i++){
			int x,y,z;
			cin>>x>>y>>z;
			G[x].push_back(P(z,y));//无向图带权值 
			G[y].push_back(P(z,x));
		}
		
		int xx,yy,zz;//无向图拆一条边变成树 
		cin>>xx>>yy>>zz;
		init(N);
		for(int i=0;i<Q;i++){
			int u,v;
			cin>>u>>v;
			long long  ans=get_dis(u,v);
			ans=min(ans,get_dis(xx,u)+get_dis(yy,v)+zz);
			ans=min(ans,get_dis(xx,v)+get_dis(yy,u)+zz);
			cout<<ans<<endl;
		}
	}
} 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值