Problem F: 挑战迷宫
Time Limit: 1 Sec Memory Limit: 128 MBSubmit: 135 Solved: 34
[ Submit][ Status][ Web Board]
Description
小翔和小明正在挑战一个神奇的迷宫。迷宫由n个房间组成,每个房间的编号为1~n,其中1号房间是他们俩初始位置,
所有房间一共由n-1条路连接,使得房间两两之间能够相互达到(构成一棵树),每条路的长度为Wi。
每当小翔和小明都在房间时,他们的神奇手机就能显示两人的位置(两人分别在哪两个房间),现在想请聪明的ACMer
快速地算出他们之间的最短距离。
所有房间一共由n-1条路连接,使得房间两两之间能够相互达到(构成一棵树),每条路的长度为Wi。
每当小翔和小明都在房间时,他们的神奇手机就能显示两人的位置(两人分别在哪两个房间),现在想请聪明的ACMer
快速地算出他们之间的最短距离。
Input
第一行输入整数n(0<n<=100000),表示迷宫有n个房间。
接下来n-1行,每行输入3个整数u,v,w(1<=u,v<=n,u!=v,w<=10000),表示编号为u和v的房间之间有一条长为w的路。
第n+1行输入整数m(0<m<=100000),表示有m次询问。
接来下m行,每行输入2个整数u,v(1<=u,v<=n),表示小翔和小明当前所在房间的编号。
接下来n-1行,每行输入3个整数u,v,w(1<=u,v<=n,u!=v,w<=10000),表示编号为u和v的房间之间有一条长为w的路。
第n+1行输入整数m(0<m<=100000),表示有m次询问。
接来下m行,每行输入2个整数u,v(1<=u,v<=n),表示小翔和小明当前所在房间的编号。
Output
对于每次询问的输出占一行,输出一个整数d表示小翔和小明之间的最短距离。
Sample Input
4
1 2 1
2 3 1
1 4 1
1
3 4
Sample Output
3
HINT
解题思路:这是一道LCA的模板题,我也是在补题的时候刚学的LCA(最近公共祖先),这里我用的是tarjan离线(还有在线的算法,还没怎么学)算法求的(后面详细说一下这个算法),这个算法的作用就是从某个根节点出发,算出每个节点到这个根节点的距离dist[i],然后两个节点间的最短距离就为
dist[x] + dist[y] - 2*dist[z],z为x和y的LCA。做题前提是你要会邻接表,不会的话可以点进去看看哈。
AC代码:
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<math.h>
#include<time.h>
#include<map>
#include<string>
#include<algorithm>
#include<set>
#include<queue>
#include<iomanip>
#define N 100005
using namespace std;
int head[N], root[N], vis[N], dist[N], m, cnt, cnt2, head2[N];
//dist[i]为节点i到根节点的距离
int x[N], y[N], z[N];
//z[i]为x[i]和y[i]的LCA
struct Edge
{
int End;
int Len;
int next;
}e[2*N],e2[2*N];
void Add_Edge(int u, int v, int w)
{
e[cnt].End = u;
e[cnt].Len = w;
e[cnt].next = head[v];
head[v] = cnt ++;
}
void Add_question(int u, int v, int i)
{
e2[cnt2].End = u;
e2[cnt2].Len = i;
e2[cnt2].next = head2[v];
head2[v] = cnt2 ++;
}
int Find_root(int k)
{
if(root[k] == k)
return root[k] = k;
return root[k] = Find_root(root[k]);
}
void tarjan(int k)
{
vis[k] = 1;
root[k] = k;
for(int i = head2[k]; i != -1 ; i = e2[i].next) { //查找LCA
if(vis[e2[i].End])
z[e2[i].Len] = Find_root(e2[i].End);
}
for(int i = head[k]; i != -1; i = e[i].next) {
if(!vis[e[i].End]) {
dist[e[i].End] = dist[k] + e[i].Len;
tarjan(e[i].End);
root[e[i].End] = k;
}
}
}
int main()
{
int n;
while(~scanf("%d", &n))
{
int u, v, w;
cnt = 0;
cnt2 = 0;
memset(head, -1, sizeof(head));
memset(head2, -1, sizeof(head2));
memset(vis, 0, sizeof(vis));
for(int i = 0; i < n - 1; i ++) {
scanf("%d%d%d", &u, &v, &w);
Add_Edge(u, v, w);
Add_Edge(v, u, w);
}
scanf("%d", &m);
for(int i = 0; i < m; i ++) { //储存要解决的问题
scanf("%d%d", &u, &v);
Add_question(u, v, i);
Add_question(v, u, i);
x[i] = u;
y[i] = v;
z[i] = 0;
}
dist[1] = 0;
tarjan(1);
for(int i = 0; i < m; i ++){ //离线解决
printf("%d\n", dist[x[i]] + dist[y[i]] - 2 * dist[z[i]]);
}
}
return 0;
}