1556:Dis——Tarjan求LCA、倍增求LCA

news2025/3/9 20:04:59

【题目描述】
给出 n 个点的一棵树,多次询问两点之间的最短距离。

注意:边是双向的。

【输入】
第一行为两个整数 n 和 m。n 表示点数,m 表示询问次数;

下来 n−1 行,每行三个整数 x,y,k,表示点 x 和点 y 之间存在一条边长度为 k;

再接下来 m 行,每行两个整数 x,y,表示询问点 x 到点 y 的最短距离。

【输出】
输出 m 行。对于每次询问,输出一行。

【输入样例】
2 2
1 2 100
1 2
2 1
【输出样例】
100
100
【提示】
样例输入 2

3 2
1 2 10
3 1 15
1 2
3 2
样例输出 2

10
25
数据范围与提示:

对于全部数据,2≤n≤104,1≤m≤2×104,0<k≤100,1≤x,y≤n。

分析

  1. 找LCA的过程和上题一致:1557:祖孙询问,代码和上题差不多,只不过多加一个距离问题,这里解释下,如何求两点之间的最短距离;
  2. 用一个数组dist存每个点到根节点的距离,通过下图发现,任意两点的距离都可转化为,他们各自到根节点的距离和 减去 2倍最近公共祖先到根节点的距离(重叠了两次);
  3. 可以采用 倍增在线求LCA、Tarjan离线求LCA;
  4. 在线做法:输入一个询问,处理一个,输出一个;离线做法:将全部询问存起来,再处理完这全部的询问,再全部输出;

在这里插入图片描述

倍增在线求LCA

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
const int N = 20010, M = N * 2;

int n, m;
int h[N], e[M], w[M], ne[M], idx;
int q[N];
//fa[i,j]表示从i向上走 2^j 步能到的点;depth表示深度
int depth[N], fa[N][21];// 2^16 >40010
int dist[N];//表示每个点到根节点的距离
void add(int a, int b, int c) {
    e[++idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx;
}

//初始化depth、fa
void bfs() {
    memset(depth, 0x3f, sizeof depth);
    //哨兵:depth[0] = 0,f[i,j]默认就是0了
    depth[0] = 0;
    int hh = 0, tt = 0;
    //第一个点——根节点
    q[0] = 1;
    depth[1] = 1;
    while (hh <= tt) {
        int u = q[hh++];
        for (int i = h[u]; ~i; i = ne[i]) {
            int v = e[i];
            //如果u,v相差不是一层,说明v还没被搜过,加队列
            if (depth[v] > depth[u] + 1) {
                depth[v] = depth[u] + 1;
                q[++tt] = v;
                //初始化fa
                fa[v][0] = u;
                for (int k = 1; k <= 20; ++k) {
                    //先跳2^(k-1)步到f[v][k-1],再跳2^(k-1)步
                    fa[v][k] = fa[fa[v][k - 1]][k - 1];
                }
                //设置dist
                dist[v] = dist[u] + w[i];
            }
        }
    }
}

//求两个点的公共祖先
int lca(int a, int b) {
    //a要在b下面
    if (depth[a] < depth[b])
        swap(a, b);
    //1. 先跳到同一层(一个数肯定能通过二进制表示,这层for结束就到了同一层)
    for (int k = 20; k >= 0; --k) {
        //a先向上跳2^k步
        //不用怕k=16或者20起始值太大,有哨兵,不怕他第一次跳太远,导致直接跳出根节点,如果跳出根节点,f[i][j]=0会出手的
        //哨兵的作用:如果f[a][k]跳出了根节点,那么左边就是depth[0]=0,自然不满足当前if,i--找下一个i
        if (depth[fa[a][k]] >= depth[b]) {
            //a赋上新位置,继续往上跳
            a = fa[a][k];
        }
    }
    //找到公共祖先
    if (a == b)
        return a;
    //2. 同时往上跳,至到他们最近公共祖先的下一层
    for (int k = 20; k >= 0; --k) {
        //说明还没跳到公共祖先
        //哨兵的作用:如果a跳出去了,b肯定也跳出去了,因为同一层开始跳的,然后fa[a][k]=0,自然不满足当前的if,i--
        if (fa[a][k] != fa[b][k]) {
            //更新这次跳到的新位置
            a = fa[a][k];
            b = fa[b][k];
        }
    }
    //当前在公共祖先的下一层,再往上跳一层即可
    return fa[a][0];//fa[b][0]也是可以的
}

int main() {
    memset(h, -1, sizeof h);
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n - 1; ++i) {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        add(a, b, c), add(b, a, c);//无向边
    }
    //从任意一点当根节点开搜即可
    bfs();
    while (m--) {
        int x, y;
        scanf("%d%d", &x, &y);
        int p = lca(x, y);
        printf("%d\n", dist[x] + dist[y] - 2 * dist[p]);
    }
    return 0;
}

Tarjan离线求LCA

这个方法是通过标记的方法求LCA,但是求距离和上面一样,求公共祖先的方式不一样;使用dfs初始化dist,从上往下搜素;
所有节点分为三种状态:
2 已经遍历过,且回溯过的点
1 代表正在搜的分支
0 还没有搜到的点
在这里插入图片描述

#include <bits/stdc++.h>

using namespace std;

typedef pair<int, int> PII;

const int N = 20010, M = N * 2;

int n, m;
int h[N], e[M], w[M], ne[M], idx;
int st[N];
int dist[N];//表示每个点到根节点的距离
int p[N]; //并查集
int ans[N];//每一组询问的答案
vector<PII> v[N]; //v[u][v][id]: first存u的邻边v,second存查询编号id

void add(int a, int b, int c) {
    e[++idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx;
}

//初始化dist
void dfs(int u, int father) {
    for (int i = h[u]; ~i; i = ne[i]) {
        //  u->v
        int v = e[i];
        //不往上搜
        if (v == father)
            continue;
        //父节点+这条边
        dist[v] = dist[u] + w[i];
        dfs(v, u);
    }
}

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

//所有节点分为三种状态:
//2 已经遍历过,且回溯过的点
//1 代表正在搜的分支
//0 还没有搜到的点
void tarjan(int u) {
    //首先标记为正在搜u这个分支
    st[u] = 1;
    for (int i = h[u]; ~i; i = ne[i]) {
        int v = e[i];
        //没被遍历过
        if (!st[v]) {
            tarjan(v);
            //回溯时进行,这句话不能写上面
            p[v] = u;  //把v合并到u
        }
    }
    //和u相关的查询
    for (auto item: v[u]) {
        int v = item.first, id = item.second;
        if (st[v] == 2) {
            int anc = find(v);//u,v的公共祖先
            ans[id] = dist[u] + dist[v] - 2 * dist[anc];
        }
    }
    //u已经遍历过且回溯过
    st[u] = 2;
}


int main() {
    memset(h, -1, sizeof h);
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n - 1; ++i) {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        //无向边
        add(a, b, c), add(b, a, c);
    }
    for (int i = 0; i < m; ++i) {
        int x, y;
        scanf("%d%d", &x, &y);
        //x=y,ans数组这个查询编号id位置的值默认就是0
        if (x != y) {
            v[x].push_back({y, i});
            v[y].push_back({x, i});
        }
    }
    //初始化p数组
    for (int i = 1; i <= n; i++)
        p[i] = i;

    //从任意一点开搜即可
    dfs(1, -1);
    tarjan(1);
    for (int i = 0; i < m; ++i) {
        cout << ans[i] << endl;
    }
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/103808.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

英文计算机论文写作,需要注意哪些细节? - 易智编译EaseEditing

&#xff08;1&#xff09;尽量用动词少用名词化 那些大篇名词的文章真的很难读懂&#xff0c;而有强大动词的句子更容易理解。 我们注意到客户在论文中经常使用的名词有“agreement”, “disagreement”, “investigation”, “analysis”, “examination”, “comparison”…

Qt 中的多项目管理

背景&#xff1a; 在Visual Studio中使用 “Solution” 来组织多个 Projects。 在Qt中&#xff0c;使用 "Subdirs Project"来组成多个projects &#xff0c;实现VS中的Solution功能。 项目类型 首先是先新建一个子目录项目&#xff0c;再根据需要在子目录项目中添…

漏洞深度分析|CVE-2022-1471 SnakeYaml 命令执行漏洞

项目介绍 YAML是一种数据序列化格式&#xff0c;设计用于人类的可读性和与脚本语言的交互。 SnakeYaml是一个完整的YAML1.1规范Processor&#xff0c;支持UTF-8/UTF-16&#xff0c;支持Java对象的序列化/反序列化&#xff0c;支持所有YAML定义的类型。 项目地址 https://gi…

javaee之MyBatis框架3

mybatis中编写dao实现类的使用方式 简单说一下实现原理&#xff1a; 下面来说一下UserDaoImpl的实现原理 mybatis主配置文件中properties标签的使用 第一种&#xff1a; <property name"driver" value"com.mysql.jdbc.Driver"></property> &…

软件加密保护:Mirage License Protector v5 注册版

Mirage License Protector v5.1.0 适用于许多编译器 License Protector 是一个 DLL / COM exe&#xff0c;几乎可以与 所有编译器一起使用&#xff1a;C、C#、.NET、Delphi、VB6 和 VB.NET&#xff0c; 32 位和 64 位的 Java 哪种产品适合我&#xff1f; 选择许可证保护器 应…

智能无障碍轮椅——PID算法控制

文章目录一、PID算法二、控制方法对比棒棒控制&#xff08;启停控制器&#xff09;比例控制PI控制PID控制三、PID的手动整定四、PID衰减曲线整定五、PID调节器各校正环节的作用是&#xff1a;六、PID算法的一般表达式是&#xff1a;七、计算注意事项一、PID算法 PID控制是最早…

图像语义分割网络FCN(32s、16s、8s)原理及MindSpore实现

一、FCN网络结构 全卷积网络(Fully Convolutional Networks)&#xff0c;是较早用于图像语义分割的神经网络。根据名称可知&#xff0c;FCN主要网络结构全部由卷积层组成&#xff0c;在图像领域&#xff0c;卷积是一种非常好的特征提取方式。本质上&#xff0c;图像分割是一个分…

DBCO-PEG-NH2/Amine二苯基环辛炔-聚乙二醇-氨基 简介。用于以高特异性和反应性标记叠氮化物修饰的生物分子。

中文名称&#xff1a; 二苯基环辛炔-聚乙二醇-氨基 氨基-聚乙二醇-二苯基环辛炔 英文简称&#xff1a; DBCO-PEG-NH2/Amine Amine/H2N-PEG-DBCO 外观&#xff1a; 灰白色固体或半固体&#xff0c;取决于PEG的分子量 溶剂&#xff1a; 部分常规有机溶剂 存储…

day20-django

文件上传 批量上传数据 案例&#xff1a;混合数据(Form) 提交页面&#xff1a;用户输入数据文件&#xff08;输入不能为空&#xff0c;报错&#xff09; django开发过程中两个特殊的文件夹 static&#xff1a;存放静态文件的路径&#xff0c;包括css、js、项目图片 media&…

【问题记录】Git问题记录

文章目录问题1Failed to connect to github.com port 443 after 21085 ms: Timed outOpenSSL SSL_read: Connection问题2unable to access https://github.com//: OpenSSL SSL_read: Connection was reset, errno 10054网速慢问题clone太慢&#xff0c;pull太慢怎么办问题1 Fa…

记录C,C++关键字的位置,直接跳过注释和字符串文本。

依据第二版本&#xff0c;可以写一个跳过注释的查找函数 C_IndexOfWord Java_IndexOfWord CSharp_IndexOfWord 还有一种方法&#xff0c;可以先把所有注释用空格代替&#xff0c;查出的字符位置也不变。 以前版本&#xff1a; DList<TextColor> Syntax::GetTextColor…

m基于matlab的连续相位调制(CPM)解调系统仿真,包括解调,同步等模块

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 随着数字通信技术的飞速发展&#xff0c;数字通信的调制技术也得到了快速发展&#xff0c;其中连续相位调制(Continuous Phase Modulation&#xff0c;CPM)作为一种全新的通信调制方法得到了越来…

GNN 极简入门

文章目录图基本知识GNN简介GCNPYG极简入门Data Handling of GraphsCommon Benchmark DatasetsMini-batchesData TransformsLearning Methods on Graphs图基本知识 &#x1f638;图是由节点的有穷非空集合和节点之间边的集合组成&#xff0c;通常表示为 G(V,E)G(V, E)G(V,E)&am…

涨知识系列:爆款短视频拍摄技巧之一,构图

拍摄技巧主要分为两个部分&#xff0c;一个是构图&#xff0c;一个是拍摄手法。这部分内容其实也是比较简单的&#xff0c;因为短视频毕竟不需要做到像拍电影那么专业。所以在短视频当中我们只要学会一些基本的技巧就完全够用了。更重要的是我们需要对构图和拍摄手法有一个概念…

基于KT6368A的双模蓝牙模块打印机的方案

目录 一、打印机蓝牙模块简介 目前主流的打印机&#xff0c;很多都还是不带蓝牙。大部分的受限于成本等等原因&#xff0c;都还是通过USB的方式和电脑进行通讯&#xff0c;从而完成打印的数据交互 因为早期蓝牙技术发展的比较缓慢&#xff0c;而打印机类型的产品&#xff0c;…

_12LeetCode代码随想录算法训练营第十二天-C++二叉树

_12LeetCode代码随想录算法训练营第十二天-C二叉树 二叉树基础知识 二叉树的种类 满二叉树 满二叉树&#xff1a;如果一棵二叉树只有度为0的结点和度为2的结点&#xff0c;并且度为0的结点在同一层上&#xff0c;则这棵二叉树为满二叉树。 完全二叉树 完全二叉树的定义如下…

线段树能解决多少问题?

背景 给一个两个数组&#xff0c;其中一个数组是 A [1,2,3,4]&#xff0c;另外一个数组是 B [5,6,7,8]。让你求两个数组合并后的大数组的&#xff1a; 最大值最小值总和 这题是不是很简单&#xff1f;我们直接可以很轻松地在 O(mn) 的时间解决&#xff0c;其中 m 和 n 分别为…

maven的java工程获取mysql数据库数据【问题及解决过程记录】

创建数据库maven&#xff0c;指定字符集和排序规则 UTF8MB4常用的排序规则&#xff1a;utf8mb4_unicode_ci、utf8mb4_general_ci、utf8mb4_bin&#xff0c;选用哪种方式呢&#xff1f;先来分析一下&#xff1a; 1、准确性&#xff1a; &#xff08;1&#xff09;utf8mb4_unico…

ffmpeg-时间基tbn、tbc、tbr

时间基的作用 源码来自ffmpeg5.1。 时间基在ffmpeg中是通过数据结构有理数AVRational描述的。时间基为时间戳的单位&#xff0c;比如时间基tbn(AVStream.time_base)0.001秒&#xff0c;AVPacket的pts40&#xff0c;则表明该AVPacket要在tbn*pts0.04秒开始显示。 /** 代码路径…

JavaScript 网页特效

一、Offset 1.1 概述 offset > 偏移量 &#xff0c;可以动态的获取的元素的位置、大小等属性。 获得元素距离带有定位父元素的位置获得元素自身的大小(宽度高度) 返回的数值都不带单位 offset常用属性&#xff1a; 属性作用element.offsetParent返回作为该元素带有定位…