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;
}