题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3320
题意描述:给你含有n个点(n<=50000)无向无环图,给出Q个查询,每个查询有三个点,问这三个点组成的最近距离为多少
分析:这三个的最近距离等于每两个点的最近距离之和除以2,因为查询太多,我们可以采用LCA离线算法来求出需要的两个点之间的距离!!
总结:离线的LCA是先将所有的查询先存放起来,然后在计算每一个的结果,但是输出时要将输入和结果一一对应起来,这个采用邻接表存放的话,输入和边就有着一一对应的关系,所以可以根据边来顺序输出结果
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int M = 50010;
struct node
{
int x,y,nxt,w;
}edge[M*2];
int head[M],e;
void addedge(int x, int y,int w)
{
edge[e].x=x;
edge[e].y=y;
edge[e].w=w;
edge[e].nxt=head[x];
head[x]=e++;
}
struct node1
{
int x,y,d,nxt;
}edge1[M*20];
int head1[M],e1;
void add(int x, int y)
{
edge1[e1].x=x;
edge1[e1].y=y;
edge1[e1].d=0;
edge1[e1].nxt=head1[x];
head1[x]=e1++;
}
int father[M];
int find(int x)
{
while(x!=father[x])
x=father[x];
return x;
}
int d[M];
bool flag[M],vis[M];
void tarjan_lca(int u)
{
int i,v;
vis[u]=true;
father[u]=u;
for(i=head[u];i!=-1;i=edge[i].nxt)
{
v=edge[i].y;
if(!vis[v])
{
d[v]=d[u]+edge[i].w;
tarjan_lca(v);
father[find(v)]=u;//找到u的子树v后将其祖先置为u
}
}
flag[u]=true;
for(i=head1[u];i!=-1;i=edge1[i].nxt)
{
v=edge1[i].y;
if(flag[v])
edge1[i].d=d[u]+d[v]-2*d[find(v)];
}
}
int main ()
{
int n;
int i,x,y,w,j,m,z;
int k=0;
while(scanf("%d",&n)!=EOF)
{
memset(head,-1,sizeof(head));
e=0;
for(i=1;i<n;i++)//建立树
{
scanf("%d%d%d",&x,&y,&w);
addedge(x+1,y+1,w);
addedge(y+1,x+1,w);
}
scanf("%d",&m);
memset(head1,-1,sizeof(head1));
e1=0;
for(j=0;j<m;j++) //建立查询树
{
scanf("%d%d%d",&x,&y,&z);
x++;y++;z++;
add(x,y);
add(y,x);
add(x,z);
add(z,x);
add(y,z);
add(z,y);
}
memset(flag,0,sizeof(flag));
memset(vis,0,sizeof(vis));
d[1]=0;
// for(i=1;i<=n;i++)
// father[i]=i;
tarjan_lca(1);//将节点1看成为根节点
if(k>0)printf("\n");
k++;
for(i=0;i<e1;i+=6)
{
int num=0;
if(edge1[i].d!=0)
num+=edge1[i].d;
else num+=edge1[i+1].d;
if(edge1[i+2].d!=0)
num+=edge1[i+2].d;
else num+=edge1[i+3].d;
if(edge1[i+4].d!=0)
num+=edge1[i+4].d;
else num+=edge1[i+5].d;
printf("%d\n",num/2);
}
}
return 0;
}