首先关于什么事Tarjan算法和最近公共祖先,要先看这篇博客,讲的很详尽了:
[最近公共祖先LCA(Tarjan算法)的思考和算法实现]
原题:
How far away ?
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 13574 Accepted Submission(s): 5090Problem Description
There are n houses in the village and some bidirectional roads connecting them. Every day peole always like to ask like this “How far is it if I want to go from house A to house B”? Usually it hard to answer. But luckily int this village the answer is always unique, since the roads are built in the way that there is a unique simple path(“simple” means you can’t visit a place twice) between every two houses. Yout task is to answer all these curious people.Input
First line is a single integer T(T<=10), indicating the number of test cases.
For each test case,in the first line there are two numbers n(2<=n<=40000) and m (1<=m<=200),the number of houses and the number of queries. The following n-1 lines each consisting three numbers i,j,k, separated bu a single space, meaning that there is a road connecting house i and house j,with length k(0 < k <= 40000).The houses are labeled from 1 to n.
Next m lines each has distinct integers i and j, you areato answer the distance between house i and house j.
Output
For each test case,output m lines. Each line represents the answer of the query. Output a bland line after each test case.Sample Input
2
3 2
1 2 10
3 1 15
1 2
2 32 2
1 2 100
1 2
2 1Sample Output
10
25
100
100Source
ECJTU 2009 Spring Contest
看了网上的代码,但还是在迷迷糊糊的情况下ac了(对我就是抄袭狗)。。。其实本质并没有弄清楚,所以还需研究
这题的做法似乎还可以用RMQ来解决,也能在线处理,干。。。我要先把Tarjan给弄清楚再说。。。
B站上的良心up主还有提供优化的倍增算法,有空也要看看:
[UESTCACM 每周算法讲堂 倍增RMQ与倍增求LCA]
反正就是好多东西都还没看就对了(〃>皿<)
下面是模拟储存的过程:
对于这样的一棵树:
1
/ | \
2 3 4
|
5
Input:
point1 point2 value
1 2 10
1 3 15
3 5 14
1 4 20
1 2 10
total: 0
save[0].from = 1
save[0].to = 2
save[0].val = 10
saev[0].next = -1(head[1] = 0)
total: 1
save[1].from = 2
save[1].to = 1
save[1].val = 10
saev[1].next = -1(head[2] = 1)
1 3 15
total: 2
save[2].from = 1
save[2].to = 3
save[2].val = 15
saev[2].next = 0(head[1] = 2)
total: 3
save[3].from = 3
save[3].to = 1
save[3].val = 15
saev[3].next = -1(head[3] = 3)
3 5 14
total: 4
save[4].from = 3
save[4].to = 5
save[4].val = 14
saev[4].next = 3(head[3] = 4)
total: 5
save[5].from = 5
save[5].to = 3
save[5].val = 14
saev[5].next = -1(head[5] = 5)
1 4 20
total: 6
save[6].from = 1
save[6].to = 4
save[6].val = 20
saev[6].next = 2(head[1] = 6)
total: 7
save[7].from = 4
save[7].to = 1
save[7].val = 20
saev[7].next = -1(head[4] = 7)
< u,v >的距离 = u到根节点的距离 + v到根节点的距离 - 2*(a,b的最近公共祖先到根节点的距离)
先贴下ac的代码:
dis[] 储存每个点到根节点的距离
head[] 是用来存储还未查询的边
LCA[n] 存储第n次询问中两个点的最近公共祖先
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 40010;
struct unit{
int from,to;
int next;
int val;
}save[2*maxn];
struct unit1{
int from,to;
int next;
int num;
}question[2*maxn];
int total,head[maxn],head1[maxn],total1,father[maxn],dis[maxn],LCA[maxn];
bool vis[maxn];
void add(int a,int b,int c){
save[total].from = a;
save[total].to = b;
save[total].next = head[a];
save[total].val = c;
head[a] = total++;
}
void add1(int a,int b,int c){
question[total1].from = a;
question[total1].to = b;
question[total1].next = head1[a];
question[total1].num = c;
head1[a] = total1++;
}
int Find(int x){
if(x==father[x]) return father[x];
return father[x] = Find(father[x]);
}
void tarjan(int u){
int i,v;
vis[u] = true;
father[u] = u;
for(i=head[u];i!=-1;i=save[i].next){
v = save[i].to;
if(!vis[v]){
dis[v] = dis[u] + save[i].val;
tarjan(v);
father[v] = u;
}
}
for(i=head1[u];i!=-1;i=question[i].next){
v = question[i].to;
if(vis[v]){
LCA[question[i].num] = Find(v);
}
}
}
int main(){
int t,i,j;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
total=0;
memset(head,-1,sizeof(head));
for(i=1;i<=n-1;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
add(b,a,c);
}
memset(vis,false,sizeof(vis));
memset(head1,-1,sizeof(head1));
total1 = 0;
for(i=1;i<=m;i++){
int a,b;
scanf("%d%d",&a,&b);
add1(a,b,i);
add1(b,a,i);
}
dis[1]=0;
tarjan(1);
for(i=0;i<total1;i+=2){
int a,b,c;
a = question[i].from;
b = question[i].to;
c = question[i].num;
printf("%d\n",dis[a]+dis[b]-2*dis[LCA[c]]);
}
}
return 0;
}