hdu2586 How far away? Tarjan(离线)算法求最近公共祖先LCA 待补完

首先关于什么事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): 5090

Problem 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 3

2 2
1 2 100
1 2
2 1

Sample Output
10
25
100
100

Source
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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值