hdu2586How far away ?(Tarjan离线LCA)

题目大意:有n个点,同n-1条带有权值的双向边相连,有m个询问,每个询问包含两个数x,y,求x与y的最短距离。

例:

Sample Input

2

3 2

1 2 10

3 1 15

1 2

2 3

 

2 2

1 2 100

1 2

2 1

 

Sample Output

10

25

100

100

 

解题思路:LCA的模板题,刚开始学。。。

用LCA求最短路时必须保证两个顶点之间只有一条边相连,否则就是图而不是树了。

因为n个节点,含有n-1条边,我们可以把它看成一颗树,然后我们把1号节点看成这颗树的根节点,这样我们计算任意两个点x,y的最短距离就可以简单表示为dis[x]+dis[y]-2*dis[lca(x,y)](其中dis[i]表示节点i到根节点的距离,lca(x,y)表示x,y的最近公共祖先)。

如果不会Tarjan离线算法可以看这个博客,http://www.cnblogs.com/JVxie/p/4854719.html,非常简单易懂。

转自https://www.cnblogs.com/zjl192628928/p/9723324.html

AC代码:

#include<iostream>
#include<vector>
#define maxn 40005
using namespace std;
struct node{
	int to,w;
};
int n,m;
vector<node> edge[maxn],que[maxn];
int vis[maxn],f[maxn],dis[maxn],ans[maxn];
void init()
{
	for(int i=1;i<=n;i++)
	{
		f[i]=i;
		vis[i]=0;
		dis[i]=0;
		edge[i].clear();
		que[i].clear();
	}
}
int find(int x)
{
	if(x==f[x]) return x;
	else return f[x]=find(f[x]);
}
void merge(int a,int b)
{
	int t1=find(a);
	int t2=find(b);
	if(t1!=t2)
	f[t1]=t2;
}
void tarjan(int x)
{
	int i;
	vis[x]=1;
	for(i=0;i<edge[x].size();i++)
	{
		int v=edge[x][i].to;
		if(!vis[v])
		{
			dis[v]=dis[x]+edge[x][i].w;
			tarjan(v);
			merge(v,x);
			vis[v]=1;
		}
	}
	for(i=0;i<que[x].size();i++)
	{
		int v=que[x][i].to;
		if(vis[v])
		ans[que[x][i].w]=dis[v]+dis[x]-2*dis[find(v)];
	}
}
int main()
{
	int t,i;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&m);
		init();
		for(i=1;i<n;i++)
		{
			int u,v,w;
			scanf("%d%d%d",&u,&v,&w);
			edge[u].push_back({v,w});
			edge[v].push_back({u,w});
		}
		for(i=1;i<=m;i++)
		{
			int a,b;
			scanf("%d%d",&a,&b);
			que[a].push_back({b,i});
			que[b].push_back({a,i});
		}
		tarjan(1);
		for(i=1;i<=m;i++)
		printf("%d\n",ans[i]);
		
	}
	return 0;
 } 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值