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

Distance Queries

Time Limit: 2000MS
Memory 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是农场编号)。最后输出所有

询问结果。

POJ1984链接:http://poj.org/problem?id=1984

思路:本题输入有些特殊,给出的是某点在某点的某个方向(东西南北)有多远。由于输入数

据比较特殊。全部是有向边,且构不成回路,所以一定会是一棵树。所以可以根据直接套用

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;
}
时间: 2024-10-15 05:52:40

POJ1986 Distance Queries【最近公共祖先】【Tarjan-LCA算法】的相关文章

笔记:LCA最近公共祖先 Tarjan(离线)算法

LCA最近公共祖先 Tarjan他贱(离线)算法的基本思路及其算法实现 本文是网络资料整理或部分转载或部分原创,参考文章如下: https://www.cnblogs.com/JVxie/p/4854719.html http://blog.csdn.net/ywcpig/article/details/52336496 https://baike.baidu.com/item/最近公共祖先/8918834?fr=aladdin 最近公共祖先简称LCA(Lowest Common Ancesto

最近公共祖先 tarjan离线算法 C++

最近做到一道题目,大概的意思就是求一个多叉树中两个节点的最近公共祖先,输入是用邻接矩阵表示的. 要想理解tarjan算法并实现它,需要先理解一下内容: 1) 深度优先搜索:tarjan算法核心思想:当某节点刚刚搜索完毕时,看与其相关的结点v是否已经被访问,如果v已经被访问过了,则它们的最近公共祖先就是v的祖先. 2) 并查集原理和实现方法,并查集的代表和祖先的区别(其实也可以一起表示),祖先的更新时刻 3) 如何表示多叉数(邻接链表,邻接矩阵),如何表示查询对,如何记录查询结果 下面是c++实现

LCA最近公共祖先 Tarjan离线算法

学习博客:  http://noalgo.info/476.html 讲的很清楚! 对于一颗树,dfs遍历时,先向下遍历,并且用并查集维护当前节点和父节点的集合.这样如果关于当前节点(A)的关联节点(B)(及要求的最近祖先的另一个点)之前被访问过,那么 B可定已经属于一个集合,先前对于访问过的点,已经维护了那个点所在集合的根,所以找到B节点所在集合的根,那么这个点就是最近的根,因为对于dfs访问的顺序.

POJ1986 Distance Queries (LCA)

传送门: http://poj.org/problem?id=1986 Distance Queries Time Limit: 2000MS   Memory Limit: 30000K       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 lifesty

最近公共祖先(LCA)---tarjan算法

LCA(最近公共祖先).....可惜我只会用tarjan去做 真心感觉tarjan算法要比倍增算法要好理解的多,可能是我脑子笨吧略略略 最近公共祖先概念:在一棵无环的树上寻找两个点在这棵树上深度最大的公共的祖先节点,也就是离这两个点最近的祖先节点. 最近公共祖先的应用:求解两个有且仅有一条确定的最短路径的路径 举个例子吧,如下图所示4和5的最近公共祖先是2,5和3的最近公共祖先是1,2和1的最近公共祖先是1. 这就是最近公共祖先的基本概念了,那么我们该如何去求这个最近公共祖先呢? Tarjan介

最近公共祖先(lca)

囧啊囧. lca的求法太多了 倍增,tarjan,st,lct,hld.... 后边三个我就不写了,其中st我没写过,估计用不上,在线用倍增,离线用tarjan就行了. 嗯. 第一种,倍增(nlogn,在线): 倍增的思想用在树上,即可以求出lca. 我们维护二维数组,f[i][j],表示i号点的第2^j号祖先,显然2^0=1也就是f[i][0]就是他的父亲 我们需要用dfs维护一个深度数组(求lca需要用) 还需要倍增求出所有的f[i][j],学过st的都应该知道,在这里f[i][j]=f[

洛谷P3379 【模板】最近公共祖先(LCA)

P3379 [模板]最近公共祖先(LCA) 152通过 532提交 题目提供者HansBug 标签 难度普及+/提高 提交  讨论  题解 最新讨论 为什么还是超时.... 倍增怎么70!!题解好像有倍- 题面这个地方写错了 无论是用RMQ+dfs还是tarjan- 为什么我的倍增超时了 求助!为什么只有70分 题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接

【原创】洛谷 LUOGU P3379 【模板】最近公共祖先(LCA) -&gt; 倍增

P3379 [模板]最近公共祖先(LCA) 题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每行包含两个正整数x.y,表示x结点和y结点之间有一条直接连接的边(数据保证可以构成树). 接下来M行每行包含两个正整数a.b,表示询问a结点和b结点的最近公共祖先. 输出格式: 输出包含M行,每行包含一个正整数,依次为每一个询问的结果. 输入输出样例 输入

洛谷 P3379 【模板】最近公共祖先(LCA) 如题

P3379 [模板]最近公共祖先(LCA) 时空限制1s / 512MB 题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每行包含两个正整数x.y,表示x结点和y结点之间有一条直接连接的边(数据保证可以构成树). 接下来M行每行包含两个正整数a.b,表示询问a结点和b结点的最近公共祖先. 输出格式: 输出包含M行,每行包含一个正整数,依次为每一个询

luogo p3379 【模板】最近公共祖先(LCA)

[模板]最近公共祖先(LCA) 题意 给一个树,然后多次询问(a,b)的LCA 模板(主要参考一些大佬的模板) #include<bits/stdc++.h> //自己的2点:树的邻接链表(静态)表示; lca 的倍增算法 //优化 log[] const int maxn=500010; int N,M,S;//S根节点标号 int head[maxn];//head[i]=k 以i为起点的第一条边是edge[k] int dep[maxn],dp[maxn][21];//dp[i][j]