POJ1986 LCA在线算法

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. 

题目大意:

农夫约翰的牛太懒了,想走一条最短的路参加马拉松.通过POJ1984http://poj.org/problem?id=1984易知该图为无环图,因此有且仅有一条路使得路径最短,寻找最近公共祖先求解即可.

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>

using namespace std;
const int N=4e4+10;
int head[N], ver[2*N], depth[2*N], first[N];//N个点完全遍历需要2*N-1次,注意设置数组大小为2*N
int dir[N], dp[2*N][20];
bool vis[N];
int tot;
struct edge{
    int u,v,w,next;
}e[2*N];

void add(int u,int v,int w,int &k)//构造图形
{
    e[k].u=u,e[k].v=v,e[k].w=w;
    e[k].next=head[u],head[u]=k++;
    swap(u,v);
    e[k].u=u,e[k].v=v,e[k].w=w;
    e[k].next=head[u],head[u]=k++;
}

void dfs(int u,int dep)//深度遍历
{
    vis[u]=1, ver[++tot]=u;
    first[u]=tot, depth[tot]=dep;
    for(int k=head[u];k!=-1;k=e[k].next)
        {
            if(!vis[e[k].v])
                {
                    int v=e[k].v,w=e[k].w;
                    dir[v]=dir[u]+w;
                    dfs(v,dep+1);
                    ver[++tot]=u,depth[tot]=dep;
                }
        }
}

void st(int n)
{
    for(int i=1;i<=n;i++)
        dp[i][0]=i;//此处赋值同时储存了农场的位置
    for(int j=1;j<=20;j++)
        {
            for(int i=1;i+(1<<(j-1))-1<=n;i++)
                {
                    int a=dp[i][j-1],b=dp[i+(1<<(j-1))][j-1];
                    depth[a]<depth[b]?dp[i][j]=a:dp[i][j]=b;//区间中深度最小的为最近公共祖先
                }
        }
}

int rmq(int x,int y)//区间查询
{
    int k=log2(y-x+1.0);
    int a=dp[x][k],b=dp[y-(1<<k)+1][k];
    return  depth[a]<depth[b]?a:b;
}

int LCA(int u,int v)
{
    int x=first[u],y=first[v];//first为u,v初次出现的位置
    if(x>y) swap(x,y);
    int res=rmq(x,y);
    return ver[res];
}

int main()
{
    int n,m,k;
    while(~scanf("%d%d",&n,&m))
        {
            int num=0;
            memset(vis,0,sizeof(vis));
            memset(head,-1,sizeof(head));
            for(int i=1;i<=m;i++)
                {
                    int u,v,w;
                    char ch[2];
                    scanf("%d%d%d%s",&u,&v,&w,ch);
                    add(u,v,w,num);
                }
            tot=0,dir[1]=0;
            dfs(1,1);
            st(2*n-1);
            scanf("%d",&k);
            while(k--)
                {
                    int u,v;
                    scanf("%d%d",&u,&v);
                    int dis=LCA(u,v);
                    printf("%d\n",dir[u]+dir[v]-2*dir[dis]);
                }
        }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/BlueDoorZz/article/details/81275056