HDU 2586 How far away ?  LCA的Tarjan离线算法

http://acm.hdu.edu.cn/showproblem.php?pid=2586

LCA的Tarjan离线算法,由于询问的次数比较多,所以用普通的最短路算法肯定行不通

思路:dfs+并查集

用两个邻接表分别保存图和需要求的点

在深搜的过程中,求出所有结点到根结点的距离保存在数组path[ ]中,把所要求的两点的距离保存在father[ ]数组里面,flag[ ]记录结点是否被搜索过

如果需求的是x,y两点间的距离,只需要求出它们的公共祖先k,则father[x,y]=path[x]+path[y]-2*path[k]

在深搜递归回来的时候用并查集压缩路径,如果递归回来到y的时候发现x已经被标记过了,由于路径已经压缩过了,则x和y的公共祖先k必为x的父节点(即find(x))

#include<stdio.h>
#include<vector>
#include<string.h>
#include<stdlib.h>
#define M 40005
using namespace std;
struct node
{
	int v,w;
};
vector<node> g[M];    //存图
vector<node> h[M];    //存需要求的数据
int path[M];
int flag[M];
int father[M];
int p[M];
int n,m;
int find(int x)
{
	return x==p[x]?x:p[x]=find(p[x]);
}
void LCA(int k)   
{
	int s,i,si,j,ch;
	for(i=0;i<g[k].size();i++)
	{
		s=g[k][i].v;
	   if(flag[s]==0)   
	   {	
		path[s]=path[k]+g[k][i].w;
		flag[s]=1;     //标记为已搜
		LCA(s);
		p[s]=k;	     //更新父结点
		for(j=0;j<h[s].size();j++)
	    {
			si=h[s][j].v;	
		if(flag[si]==1&&father[h[s][j].w]==0)  //判断当前数据是否能够处理,且是否已经处理过了,处理过的将不再处理
		{   
			if(si==s) father[h[s][j].w]=0;  //相同结点间的距离为0
			else
			{
				father[h[s][j].w]=path[si]+path[s]-2*path[find(si)];  
			
			}
		}
	      }
	   }
	}
}
int main()
{
	int t,a,b,c,i;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&m);
		for(i=1;i<=n;i++)
		{
			g[i].clear();
			h[i].clear();
			path[i]=0;
			flag[i]=0;
			p[i]=i;
			father[i]=0;
		}
		node t;
		for(i=1;i<n;i++)   
		{
			scanf("%d%d%d",&a,&b,&c);
			t.v=a;t.w=c;
			g[b].push_back(t);
			t.v=b;
			g[a].push_back(t);

		}
		for(i=1;i<=m;i++)
		{
			scanf("%d%d",&a,&b);
			t.v=a;t.w=i;
			h[b].push_back(t);
			t.v=b;
			h[a].push_back(t);
		}
		path[1]=0;
		flag[1]=1;
		LCA(1);
		for(i=1;i<=m;i++)
			printf("%d\n",father[i]);
	}
	return 0;
}


 

 

你可能感兴趣的:(HDU 2586 How far away ?  LCA的Tarjan离线算法)