1. 程式人生 > >POJ1986 Distance Queries【最近公共祖先】【Tarjan-LCA演算法】

POJ1986 Distance Queries【最近公共祖先】【Tarjan-LCA演算法】

Distance Queries Time Limit: 2000MSMemory Limit: 30000K Total Submissions: 9777Accepted: 3425 Case Time Limit: 1000MS

Description

Farmer John's cows refused to run in his marathon since he chose a path much too long for their leisurely lifestyle. He therefore wants to find a path of a more reasonable length. The input to this problem consists of the same input as in "Navigation Nightmare",followed by a line containing a single integer K, followed by K "distance queries". Each distance query is a line of input containing two integers, giving the numbers of two farms between which FJ is interested in computing distance (measured in the length of the roads along the path between the two farms). Please answer FJ's distance queries as quickly as possible! 

Input
* Lines 1..1+M: Same format as "Navigation Nightmare" 

* Line 2+M: A single integer, K. 1 <= K <= 10,000 

* Lines 3+M..2+M+K: Each line corresponds to a distance query and contains the indices of two farms. 

Output

* Lines 1..K: For each distance query, output on a single line an integer giving the appropriate distance. 

Sample Input
7 6
1 6 13 E
6 3 9 E
3 5 7 S
4 1 3 N
2 4 20 W
4 7 2 S
3
1 6
1 4

2 6

Sample Output
13
3

36

Hint

Farms 2 and 6 are 20+3+13=36 apart. 

Source

USACO 2004 February

題目大意:John是一個農場主,他的牛很懶,拒絕按照John選的路走。John不得不找一條

最短的路。這道題的輸入前半部分和POJ1984"Navigation Nightmare"相同。在每組資料

之後是一個整數K,接下來K行是詢問(u,v)的曼哈頓距離(u,v是農場編號)。最後輸出所有

詢問結果。

思路:本題輸入有些特殊,給出的是某點在某點的某個方向(東西南北)有多遠。由於輸入數

據比較特殊。全部是有向邊,且構不成迴路,所以一定會是一棵樹。所以可以根據直接套用

Tarjan-LCA演算法的模板,考慮樹上每對節點的最短路徑計算。如果確定根為1,則有

Dist(u,v) = Dist(1,u) + Dist(1,v) - 2*Dist( 1,LCA(u,v) )。先深搜一次遍歷求出每個

節點到根結點的距離,再做一次LCA,就可以得出結果了。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN = 80080;
const int MAXQ = 20020;

int father[MAXN],Head[MAXN],QHead[MAXN],Dist[MAXN];

struct EdgeNode
{
    int to;
    int next;
    int lca;
}Edges[MAXN];

EdgeNode QEdges[MAXN];

int find(int x)
{
    if(x != father[x])
        father[x] = find(father[x]);
    return father[x];
}

bool vis[MAXN];

void LCA(int u)
{
    father[u] = u;
    vis[u] = true;
    for(int k = Head[u]; k != -1; k = Edges[k].next)
    {
        if(!vis[Edges[k].to])
        {
            Dist[Edges[k].to] = Dist[u] + Edges[k].lca;
            LCA(Edges[k].to);
            father[Edges[k].to] = u;
        }
    }

    for(int k = QHead[u]; k != -1; k = QEdges[k].next)
    {
        if(vis[QEdges[k].to])
        {
            //QEdges[k].lca = find(QEdges[k].to);
            QEdges[k].lca = Dist[u] + Dist[QEdges[k].to] - 2*Dist[find(QEdges[k].to)];
            QEdges[k^1].lca = QEdges[k].lca;
        }
    }
}

int main()
{
    int N,M,K,u,v,w,a,b;
    char s;
    while(~scanf("%d%d",&N,&M))
    {
        memset(father,0,sizeof(father));
        memset(Head,-1,sizeof(Head));
        memset(QHead,-1,sizeof(QHead));
        memset(vis,false,sizeof(vis));
        memset(Edges,0,sizeof(Edges));
        memset(QEdges,0,sizeof(QEdges));
        memset(Dist,0,sizeof(Dist));
        int id = 0;
        for(int i = 0; i < M; ++i)
        {
            scanf("%d%d%d %c",&u,&v,&w,&s);
            Edges[id].to = v;
            Edges[id].lca = w;
            Edges[id].next = Head[u];
            Head[u] = id++;
            Edges[id].to = u;
            Edges[id].lca = w;
            Edges[id].next = Head[v];
            Head[v] = id++;
        }
        scanf("%d",&K);
        int iq = 0;
        for(int i = 0; i < K; ++i)
        {
            scanf("%d%d",&a,&b);
            QEdges[iq].to = b;
            QEdges[iq].next = QHead[a];
            QHead[a] = iq++;
            QEdges[iq].to = a;
            QEdges[iq].next = QHead[b];
            QHead[b] = iq++;
        }
        LCA(1);
        for(int i = 0; i < iq; i+=2)
            printf("%d\n",QEdges[i].lca);
    }

    return 0;
}