HDU 2586 How far away? Tarjan算法 并查集 LCA

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 23506    Accepted Submission(s): 9329

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

本題大意:

  給出一棵樹,和樹邊長度,給出一系列詢問點,求兩點間的距離。

解題思路:

  本題可用Tarjan算法求出根節點到各點的最短距離,然後結合并查集來先求出最近公共祖先(LCA),這樣兩個點之間的距離就可以表示為:根節點到點1的距離 + 根節點到點2的距離 - 兩倍的根節點到LCA的距離。

  思想核心是求LCA,所以本文記錄一個用Tarjan結合并查集求LCA的方法。

  實現方法:選定一個點為根節點,對樹進行DFS搜索,搜索回溯時把子節點的并查集信息連到父節點上去。在搜索每個點的時候,都檢查是否是詢問點,如果是詢問點,則檢查與之對應的詢問點是否被訪問過,如果訪問過,那麼它的并查集父親必然到知道他們的公共祖先上去,為什麼?這是我參考其他大神代碼的時候思考一段時間才想出來為什麼的。根據DFS搜索的特性,搜索回溯時都把下級的并查集信息往上連,而DFS是深度優先,則回溯距離必然盡可能小,即如果回溯一級就找到另一條路的話,就會繼續往下搜,而如果這兩條路上各存著詢問點對的其中一個,先被搜索到的那個節點的并查集信息只往上并了一級,那就是最近的公共祖先。在處理尋問點對的時候,如果找到這樣的并查集被修改過的(即被訪問過的)的對應點,那就把它們兩個的LCA記下即可。

  把整棵樹遍歷完的時候,所有詢問點對的LCA也都求出來了,最後按照尋問順序,按照公式計算并輸出距離即可。

結合一下AC代碼理解:

#include<bits/stdc++.h>
using namespace std;
#define N 40000

typedef struct Edge
{
    int t,v;
    int next;
}edge;

edge E[N*2],e[N*2];
int cnt;
int head1[N],head2[N];//head1用于樹上節點的串連,head2用於詢問節點的串連
int dis[N],f[N],vis[N];//dis各點到根節點的距離,f為并查集
int ans[N][3];          //ans[0]和[1]記錄詢問點對,ans[3]記錄他們的公共祖先(LCA)
int n,m;
void addedge(int u,int v,int d,edge *a,int *head)
{
    a[cnt].t=v;
    a[cnt].v=d;
    a[cnt].next=head[u];
    head[u]=cnt++;

    a[cnt].t=u;
    a[cnt].v=d;
    a[cnt].next=head[v];
    head[v]=cnt++;
}

int findx(int a)//并查集
{
    if(a!=f[a])
        return f[a]=findx(f[a]);
    return a;
}

void getLCA(int p)//Tarjan算法求LCA
{
    vis[p]=1;
    f[p]=p;        //進入時初始化并查集

    for(int i=head2[p];i!=-1;i=e[i].next)//檢查詢問點對,是否發現已訪問
    {
        if(vis[e[i].t])
            ans[e[i].v][2]=findx(e[i].t);
    }

    for(int i=head1[p];i!=-1;i=E[i].next)//DFS搜索
    {
        if(!vis[E[i].t])
        {
            dis[E[i].t]=dis[p]+E[i].v;  //更新距離
            getLCA(E[i].t);
            f[E[i].t]=p;                //搜索過后更新子節點的并查集
        }
    }
}

int main()
{
    //freopen("a.txt","r",stdin);
    int T;
    scanf("%d",&T);
    while(T--)
    {
        cnt=0;
        memset(head1,-1,sizeof(head1));
        memset(head2,-1,sizeof(head2));
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n-1;i++)
        {
            int a,b,v;
            scanf("%d%d%d",&a,&b,&v);
            addedge(a,b,v,E,head1);
        }
        cnt=0;
        for(int i=1;i<=m;i++)       //先全部記錄后離線處理
        {

            scanf("%d%d",&ans[i][0],&ans[i][1]);
            addedge(ans[i][0],ans[i][1],i,e,head2);
        }

        memset(vis,0,sizeof(vis));
        dis[1]=0;
        getLCA(1);

        for(int i=1;i<=m;i++)
            printf("%d\n",dis[ans[i][1]]+dis[ans[i][0]]-2*dis[ans[i][2]]);
    }       //最短距離 = 節點1的距離 + 節點2的距離 - 2*LCA的最短距離
}           //(這裡最短距離是指導根節點的距離)

原文地址:https://www.cnblogs.com/Lin88/p/9510766.html

时间: 2024-10-11 06:58:01

HDU 2586 How far away? Tarjan算法 并查集 LCA的相关文章

hdu 1269 迷宫城堡 (tarjan算法学习)

迷宫城堡 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 7056    Accepted Submission(s): 3137 Problem Description 为了训练小希的方向感,Gardon建立了一座大城堡,里面有N个房间(N<=10000)和M条通道(M<=100000),每个通道都是单向的,就是说若称某通道连通了A

hdu 1829 A Bug&#39;s Life (基础并查集)

题目: 链接:点击打开链接 题意: 给定虫子的交配关系,确定实验是否支持教授的假设即没有同性恋或者不符合假设. 思路: 是一道基础的并查集题目.存在两个集合异性和同性,给出多组关系,看这两个集合有木有联系,即是否有同性恋. 定义一个数组sex[],sex[i]表示与编号i的性别相反的虫子编号.然后将和i虫子有联系的合并为同一个集合(认为是同性的).如果findset(u) == findset(v),出现了反常行为. 代码: #include <iostream> #include <c

HDU 1829 A Bug&#39;s Life (并查集)

题目链接:请戳这里. 题目大意及思路:给定n个Bugs(shen me gui)和m对Bugs之间的关系,假定关系是两个Bugs的男女关系,那么问存不存在同性恋的情况. 那么若a与b是男女关系,b与c是男女关系,那么a和c的性别我们就可以认为是相同的.我们用可以建立两个并查集,一类放男男关系,一类放女女关系. 那么若男男关系中出现了环的情况(即有共同的根),那么同性恋关系就出现了. #include<iostream> #include<cstdio> #include<cs

hdu 1198 Farm Irrigation (搜索或并查集)

Farm Irrigation Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 5818    Accepted Submission(s): 2521 Problem Description Benny has a spacious farm land to irrigate. The farm land is a rectangle

HDU 1272: 小希的迷宫(并查集)

小希的迷宫 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 25010    Accepted Submission(s): 7683 Problem Description 上次Gardon的迷宫城堡小希玩了很久(见Problem B),现在她也想设计一个迷宫让Gardon来走.但是她设计迷宫的思路不一样,首先她认为所有的通道都应该是

HDU 2120 Ice_cream&#39;s world I【并查集】

解题思路:给出n对点的关系,求构成多少个环,如果对于点x和点y,它们本身就有一堵墙,即为它们本身就相连,如果find(x)=find(y),说明它们的根节点相同,它们之间肯定有直接或间接的相连,即形成环 样例的示意图 共3个环 Ice_cream's world I Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 642    Acce

HDU 1102 Constructing Roads (裸的并查集)

Constructing Roads Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 13210    Accepted Submission(s): 4995 Problem Description There are N villages, which are numbered from 1 to N, and you should

hdu 1598 find the most comfortable road (并查集 + 枚举)

题目: 链接:点击打开链接 思路: 对边排序,再枚举每条边,如果出现通路(findset(x) == findset(y))就结束. 代码: #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define MAXN 220 #define MAXM 1010 #define MAX 100000000 stru

HDU 1598 find the most comfortable road (并查集||最短路)

find the most comfortable road Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 3720    Accepted Submission(s): 1583 Problem Description XX星有许多城市,城市之间通过一种奇怪的高速公路SARS(Super Air Roam Structure---超级