【LCA求最近公共祖先+vector构图】Distance Queries

Distance Queries

时间限制: 1 Sec  内存限制: 128 MB

题目描述

约翰的奶牛们拒绝跑他的马拉松,因为她们悠闲的生活不能承受他选择的长长的赛道。因此他决心找一条更合理的赛道。此题的输入于第一题相同,紧接着下一行输入一个整数K,以后K行为K个"距离问题"。每个距离问题包括两个整数,就是约翰感兴趣的两个农场的编号,请你尽快算出这两地之间的距离。

N个点,N-1条边

输入

第1行:两个分开的整数:N和M;

第2..M+1行:每行包括4个分开的内容,F1,F2,L,D分别描述两个农场的编号,道路的长度,F1到F2的方向(N,E,S,W)。

第2+M行:一个整数K。(1<=k<=10,000)

第3+M..2+M+K:每行表示一个问题,包含两个整数代表两个农场。

输出

第1..K行:对应每个问题,输出单独的一个整数给出正确的距离。

样例输入

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

样例输出

13

3

36

提示

农场2 到6 有 20+3+13=36 的距离。

题解:

首先不得不吐槽一下这道题输入数据中的方向没什么用……

然后由于这道题需要用到LCA求最近公共祖先的算法,在这里分享一下连接:倍增算法    tarjan算法

了解了算法之后,这道题就很容易解决了,我用的是tarjan算法(个人觉得比较好理解)。

1.用邻接链表建图。

2.用邻接链表存储每一个询问。(本人在这里尝试了一下STL中的vector存图,稍后介绍)

3.维护一个dis数组保存到根节点的距离。

4.每一次求出一个LCA后用dis[root]+dis[y]-dis[lca]*2来求出两点之间的距离。(原理也很简单,两个点到根节点的距离和减去最近公共祖先到根节点的距离就是这两个节点的距离)

5.按照题目要求的顺序输出结果就行了。

以上是这道题目的说明,下面我们浅谈vector的用法:

1.vector是C++中一个十分有用的容器,实际上是一个没有容量限制的数组。(但是比较耗时间,事实上STL模版基本上都比手打的要耗时)

2.定义在头文件#include<vector>中。

3.在程序中用vector<"类型">"名称";来定义。如果是vector<int>mem[101];表示的就是一个int类型的二维数组。(当然,类型可以为其他类型,包括结构体)

4.当前容量大小:mem.size();

 清空:mem.clear();

 插入元素:mem.insert(mem.begin()+i,a);在第i+1个元素前加上a。

 删除元素:mem.erase(mem.begin()+i);删除第i+1个元素。

5.使用迭代访问器访问元素:j为地址类型。

vector<student>::iterator j;
    for(j=q[root].begin();j!=q[root].end();j++)
    {
        cout<<*j<<endl;
    }

6.使用下标访问元素

int end=mem.size();
for(i=0;i<end;i++)
{
    cout<<mem[i]<<endl;
}

好了,说了这么多,终于可以上代码了,以下是AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<ctime>
#include<queue>
#include<stack>
#include<vector>
using namespace std;
const int N=40001;
const int M=10001;
int n,m,l;
struct node
{
    int next,to,dis;
}edge[2*N];
int head[N],size,vis[N],ans[M],dis[N],father[N];
struct student
{
    int x,id;
};
vector<student>q[N];
void putin(int from,int to,int dis)
{
    size++;
    edge[size].next=head[from];
    edge[size].to=to;
    edge[size].dis=dis;
    head[from]=size;
}
int find(int x)
{
    if(father[x]==x)return x;
    else
    {
        father[x]=find(father[x]);
        return father[x];
    }
}
void dfs(int root)
{

    vis[root]=1;
    vector<student>::iterator j;
    for(j=q[root].begin();j!=q[root].end();j++)
    {
        int y=j->x;
        if(vis[y])
        {
            int p=j->id;
            int lca=find(y);
            ans[p]=dis[root]+dis[y]-2*dis[lca];
        }
    }
    for(int i=head[root];i;i=edge[i].next)
    {
        int y=edge[i].to;
        if(!vis[y])
        {
            dis[y]=dis[root]+edge[i].dis;
            dfs(y);
            father[y]=root;
        }
    }
}
int main()
{
    int i,j,from,to,dis;
    char s[2];
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;i++)
    {
        scanf("%d%d%d%s",&from,&to,&dis,s);
        putin(from,to,dis);putin(to,from,dis);
    }
    scanf("%d",&l);
    for(i=1;i<=l;i++)
    {
        scanf("%d%d",&from,&to);
        q[from].push_back((student){to,i});
        q[to].push_back((student){from,i});
    }
    for(i=1;i<=n;i++)father[i]=i;
    dfs(1);
    for(i=1;i<=l;i++)
    {
        printf("%d\n",ans[i]);
    }
    return 0;
}
时间: 2024-10-07 21:11:21

【LCA求最近公共祖先+vector构图】Distance Queries的相关文章

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

一.梳理概念 定义:对于有根树T的两个结点u.v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u.v的祖先且x的深度尽可能大. 通俗地讲,最近公共祖先节点,就是两个节点在这棵树上深度最大的公共的祖先节点,即两个点在这棵树上距离最近的公共祖先节点. 提示:父亲节点也是祖先节点,节点本身也是它的祖先节点. 给出一棵树,如图所示: 由上面的定义可知:3和5的最近公共祖先为1,5和6的最近公共祖先为2,2和7的最近公共祖先为2, 6和7的最近公共祖先为4. 二.繁文缛节 注意注意注意!!!尚

LCA(最近公共祖先)算法

参考博客:https://blog.csdn.net/my_sunshine26/article/details/72717112 首先看一下定义,来自于百度百科 LCA(Lowest Common Ancestors),即最近公共祖先,是指在有根树中,找出某两个结点u和v最近的公共祖先. 注意:这里某个节点本身也是它的祖先节点. 求最近公共祖先的算法: 1.暴力:每次查询的时间复杂度为O(N) 2.Tarjan(离线)算法:在一次遍历中把所有查询解决,预处理时间复杂度O(nlogn),每次查询

求最近公共祖先(LCA)板子 x

LCA目前比较流行的算法主要有tarjian,倍增和树链剖分 1)tarjian 是一种离线算法,需要提前知道所有询问对 算法如下 1.读入所有询问对(u,v),并建好树(建议邻接表) 2.初始化每个节点各属一个并查集,都指向自己 3.对整棵树进行dfs(深度优先搜索)遍历 每处理到一个新节点(u)时看他的另一半(询问对象v)是否visit过,如果visit过了,则这组询问对的lca即v的并查集的根节点,若没有visit过,则继续向下深搜,该节点记为已visit 每当回溯的时候都将子节点的并查集

LCA 在线倍增法 求最近公共祖先

第一步:建树  这个就不说了 第二部:分为两步  分别是深度预处理和祖先DP预处理 DP预处理: int i,j; for(j=1;(1<<j)<n;j++) for(int i=0;i<n;++i) if(fa[i][j]=-1) fa[i][j]=fa[fa[i][j-1]][j-1];/*DP处理出i的2^j祖先是谁*/ 深度预处理: 1 void dfs(int now,int from,int deepth) 2 { 3 deep[now]=deepth; 4 for(i

用“倍增法”求最近公共祖先(LCA)

1.最近公共祖先:对于有根树T的两个结点u.v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u.v的祖先且x的深度尽可能大.2.朴素算法:记录下每个节点的父亲,使节点u,v一步一步地向上找父亲,直到找到相同的“祖先”,即 是所求的答案,时间复杂度O(n).3.优化算法(倍增法):利用二进制的思想,想办法使一步一步向上搜变成以2^k地向上跳. 所以定义一个P[][]数组,使p[i][j]表示节点i的2^j倍祖先,因此p[i][0]即为i的父亲. 我们可以得到一个递推式p[i][j]=p

LCA(最近公共祖先)--tarjan离线算法 hdu 2586

HDU 2586 How far away ? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 11320    Accepted Submission(s): 4119 Problem Description There are n houses in the village and some bidirectional roads c

HDU 2586 How far away ?(LCA模板 近期公共祖先啊)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2586 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

HDU 2586 How far away ?(LCA模板 最近公共祖先啊)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2586 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

lca(最近公共祖先(在线)) 倍增法详解

转自大佬博客 : https://blog.csdn.net/lw277232240/article/details/72870644 描述:倍增法用于很多算法当中,通过字面意思来理解 LCA是啥呢  在一棵树当中 lca表示的是两个节点最近公共祖先, 大家看这课树哈节点5 ,3的lca就是1,13和11的LCA就是6.节点8,12的lca就是8,那么我们如何通过被增来实现LCA呢. 首先请大家认真看下面的分析. depth[x],表示x节点的深度. 大家看下这个数组 grand[x][i] ,