AcWing 1644. 二叉树中的最低公共祖先 题解 线性dp 倍增算法 前序中序构造二叉树

二叉树中的最低公共祖先

题目描述

树中两个结点 U 和 V 的最低公共祖先(LCA)是指同时具有 U 和 V 作为后代的最深结点。给定二叉树中的任何两个结点,请你找到它们的 LCA。

输入描述

第一行包含两个整数 M 和 N ,分别表示询问结点对数以及二叉树中的结点数量。
接下来两行,每行包含 N 个不同的整数,分别表示二叉树的中序和前序遍历。保证二叉树可由给定遍历序列唯一确定。
接下来 M 行,每行包含两个整数 U 和 V ,表示一组询问。

输出描述

对于每对给定的 U 和 V ,输出一行结果:
如果 U 和 V 的 LCA 是 A ,且 A 不是 U 或 V ,则输出 “LCA of U and V is A.”
如果 U 和 V 的 LCA 是 A ,且 A 是 U 或 V 中的一个,则输出 “X is an ancestor of Y.” ,其中 X 表示 A , Y 表示另一个结点。
如果 U 或 V 没有在二叉树中找到,则输出 “ERROR: U is not found.” 或 “ERROR: V is not found.” 或 “ERROR: U and V are not found.”。

用例输入

6 8
7 2 3 4 6 5 1 8
5 3 7 2 6 4 8 1
2 6
8 1
7 9
12 -3
0 8
99 99

用例输出

LCA of 2 and 6 is 3.
8 is an ancestor of 1.
ERROR: 9 is not found.
ERROR: 12 and -3 are not found.
ERROR: 0 is not found.
ERROR: 99 and 99 are not found.

数据规模与约定

所有结点权值均在 int 范围内。
1 ≤ M ≤ 1000,1 ≤ N ≤ 10000。

题目链接

AcWing1644——传送门

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn = 1e4 + 6;

vector<int> e[maxn];    // 以第i个节点为起点的有向边
int depth[maxn];        // 第i个节点的深度
int ancestor[maxn][23]; // ancestor[i][j]表示节点i的2^j级祖先
int lg[maxn];           // 预处理第i个节点的lg值

void add(int x, int y) // 加有向边函数
{
    e[x].push_back(y);
}

void dfs(int u, int fath) // dfs计算深度和祖先
{
    ancestor[u][0] = fath;                                    // 第1级祖先即为父亲
    depth[u] = depth[fath] + 1;                               // 深度为父亲深度+1
    for (int j = 1; j <= lg[depth[u]]; j++)                   // 计算该节点的2^j级祖先
        ancestor[u][j] = ancestor[ancestor[u][j - 1]][j - 1]; // 祖先的转移方程,u的2^j祖先等于u的2^(j-1)祖先的2^(j-1)祖先
    for (int i = 0; i < e[u].size(); i++)                     // 递归子节点
    {
        int v = e[u][i];
        if (fath != v)
            dfs(v, u);
    }
}

map<int, int> idx; // 离散化后的下标
map<int, int> num; // 下标所对应的原来的值

void LCA(int x, int y) // 计算x和y的LCA
{
    // 将x和y先记录下来,便于输出答案
    int u = x;
    int v = y;
    // 将x和y转化为离散后的下标
    x = idx[x];
    y = idx[y];
    bool tag = 0;            // 记录是否发生了交换
    if (depth[x] < depth[y]) // 让x为深度更大(或相等)的那一个
    {
        swap(x, y);
        tag = 1;
    }
    while (depth[x] > depth[y]) // 将x提到与y相同深度
        x = ancestor[x][lg[depth[x] - depth[y]] - 1];
    if (x == y)
    {
        if (tag == 0)
            cout << v << " is an ancestor of " << u << "." << '\n';
        else
            cout << u << " is an ancestor of " << v << "." << '\n';
        return;
    }
    for (int k = lg[depth[x]] - 1; k >= 0; k--) // 倍增跳跃
        if (ancestor[x][k] != ancestor[y][k])
            x = ancestor[x][k], y = ancestor[y][k];
    cout << "LCA of " << u << " and " << v << " is " << num[ancestor[x][0]] << "." << '\n';
    return;
}

map<int, int> mp;
int lc[maxn];
int rc[maxn];
int traversal(int pre_st, int pre_end, int in_st, int in_end, vector<int> &in, vector<int> &pre)
{
    int cur_len = pre_end - pre_st; // 当前区间长度
    if (cur_len == 0)
        return 0;
    int root = pre[pre_st]; // 找到根节点
    if (cur_len == 1)
        return root;
    int pos = mp[root];    // 根位置
    int len = pos - in_st; // 左子树长度
    // 递归构造左子树和右子树
    lc[root] = traversal(pre_st + 1, pre_st + len + 1, in_st, pos, in, pre);
    rc[root] = traversal(pre_st + len + 1, pre_end, pos + 1, in_end, in, pre);
    return root;
}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    int n, m, root; // 节点个数,询问个数,根节点
    cin >> m >> n;
    vector<int> in(n);
    vector<int> pre(n);
    map<int, bool> exist; // 记录某点是否存在
    // 中序序列
    for (int i = 0; i < n; i++)
    {
        cin >> in[i];
        exist[in[i]] = 1;
    }
    // 离散化各个点为1~n
    int i = 1;
    for (auto it = exist.begin(); it != exist.end(); it++, i++)
    {
        idx[it->first] = i;
        num[i] = it->first;
    }
    // 将中序序列改为离散后的下标
    for (int i = 0; i < n; i++)
    {
        in[i] = idx[in[i]];
    }
    // 记录在中序序列中的位置
    for (int i = 0; i < n; i++)
    {
        mp[in[i]] = i;
    }
    // 前序序列
    for (int i = 0; i < n; i++)
    {
        cin >> pre[i];
    }
    // 将前序序列改为离散后的下标
    for (int i = 0; i < n; i++)
    {
        pre[i] = idx[pre[i]];
    }
    // 构造二叉树
    root = traversal(0, n, 0, n, in, pre);
    // 记录树中的边
    for (int i = 1; i <= n; i++)
    {
        if (lc[i] != 0)
        {
            add(i, lc[i]);
            add(lc[i], i);
        }
        if (rc[i] != 0)
        {
            add(i, rc[i]);
            add(rc[i], i);
        }
    }
    // 预处理每个深度值的lg值
    for (int i = 1; i <= n; ++i)
        lg[i] = lg[i - 1] + (1 << lg[i - 1] == i); // 非常厉害的转移方程
    // 预处理节点i的2^j级祖先
    dfs(root, 0);
    // 处理询问
    int x, y;
    for (int i = 1; i <= m; ++i)
    {
        cin >> x >> y;
        if (exist[x] == 0 && exist[y] == 0)
        {
            cout << "ERROR: " << x << " and " << y << " are not found." << '\n';
        }
        else if (exist[x] == 0)
        {
            cout << "ERROR: " << x << " is not found." << '\n';
        }
        else if (exist[y] == 0)
        {
            cout << "ERROR: " << y << " is not found." << '\n';
        }
        else
        {
            LCA(x, y);
        }
    }

    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HFSS学习-day2-T形波导的优化设计

入门实例–T形波导的内场分析和优化设计 HFSS--此实例优化设计 优化设计要求1. 定义输出变量Power31、Power21、和Power11&#xff0c;表示Port3、Port2、Port1的输出功率2.参数扫描分析添加扫描变量和输出变量进行一个小设置添加输出变量进行扫描分析 3. 优化设计&#xff0c…

第十篇:数字堡垒:操作系统安全深度解析与实战指南

数字堡垒&#xff1a;操作系统安全深度解析与实战指南 1 *引言 1.1 数字世界的守护者 在遥远的比特海中&#xff0c;有一座名为“操作系统”的数字堡垒&#xff0c;它守护着我们的数据宝藏&#xff0c;确保每一次计算的航行都能安全抵达彼岸。然而&#xff0c;这片海域并非风…

Javaweb第五次作业

poet数据库sql语言 create table poet(id int unsigned primary key auto_increment comment ID,name varchar(10) not null comment 姓名,gender tinyint unsigned not null comment 性别, 说明: 1 男, 2 女,dynasty varchar(10) not null comment朝代,title varchar(20) not…

Flume进阶

目录 第1关&#xff1a;拦截器的使用 第2关&#xff1a;自定义拦截器 第1关&#xff1a;拦截器的使用 代码文件&#xff1a; # Define source, channel, sink #agent名称为a1# Define source #source类型配置为avro,监听8888端口&#xff0c;后台会自动发送数据到该端口 #拦截后…

248 基于matlab的GA-RBF神经网络预测

基于matlab的GA-RBF神经网络预测&#xff0c;遗传算法优化来训练RBF网络权值&#xff0c;RBF优化后的结果用于预测。输出真实值、RBF预测结果、GA-RBF预测结果&#xff0c;并进行对比。程序已调通&#xff0c;可直接运行。 248 RBF神经网络 GA-RBF 时间序列预测 - 小红书 (xiao…

OpenSSL实现AES的ECB和CBC加解密,可一次性加解密任意长度的明文字符串或字节流(QT C++环境)

本篇博文讲述如何在Qt C的环境中使用OpenSSL实现AES-ECB/CBC-Pkcs7加/解密&#xff0c;可以一次性加解密一个任意长度的明文字符串或者字节流&#xff0c;但不适合分段读取加解密的&#xff08;例如&#xff0c;一个4GB的大型文件需要加解密&#xff0c;要分段读取&#xff0c;…

Linux-笔记 i2c-tools

1、i2c-tools介绍 1、在日常linux开发中&#xff0c;有时候需要确认i2c硬件是否正常连接&#xff0c;设备是否正常工作&#xff0c;设备的地址是多少等等&#xff0c;这里我们就需要使用一个用于测试I2C总线的工具——i2c-tools&#xff0c;i2c-tools原理是通过操作/dev 路径 …

JavaScript之数据类型(2)——复杂类型(object)

object的介绍&#xff1a; 我对于object的理解是和C/C中的结构体一样&#xff0c;是一个自定义的数据类型&#xff0c;我们可以通过多个简单的数据类型来定义一个便于我们使用的新的数据类型。 在网上某佬对于其解释如下&#xff1a; Object类型&#xff0c;我们也称为一个对象…

Redis学习5——Redis应用之签到

Redis位图bitMap 位图由一系列二进制位组成&#xff0c;每个位可以被设置为1或0&#xff0c;当我们在处理需要高效存储和操作大量二进制位数据的适合&#xff0c;位图是一个非常有用的工具。 位图操作命令有&#xff1a; SETBIT&#xff1a;设置位图中指定位置的位的值。可以…

3.ERC4626

ERC4626是一个vault&#xff0c;在DAI中&#xff0c;使用ETH换取DAI。其流程为先充值ETH到maker vault。 Vault 资产的管理、分红用户充值某项资产获取某个凭证该凭证作为分红、推出的依据Yield Farming/借贷/质押等 以太坊改进提案EIP:ethereum improvemwnt proposal 最初E…

Mysql8.0.30一次表锁问题的解决

起因 给material_config_field_data表的字段建立全文索引的时&#xff0c;发现该表卡死&#xff0c;然后无法对该表进行任何操作。 查找问题 执行sql #这个命令会显示InnoDB存储引擎的详细状态信息&#xff0c;包括锁等待和锁争用的信息 SHOW ENGINE INNODB STATUS结果 复制S…

​Inf-DiT:Upsampling Any-Resolution Image、Vidu、MVDiff、Trio-ViT

本文首发于公众号&#xff1a;机器感知 ​Inf-DiT&#xff1a;Upsampling Any-Resolution Image、Vidu、MVDiff、Trio-ViT Inf-DiT: Upsampling Any-Resolution Image with Memory-Efficient Diffusion Transformer Diffusion models have shown remarkable performance in im…

树莓派4-使用systemctl设置开机自启oled播放服务ip地址与logo

一、目标&#xff1a; 开机自启oled显示服务ip与端口&#xff0c;并播放logo 二、过程&#xff1a; 1、出现luma库不存在问题&#xff0c;修改.service文件&#xff0c;增加用户与用户组。在本地测试过程中可以使用python script.py执行python脚本&#xff0c;所以将.servic…

java递归-(迷宫问题)

前面 这里我们来玩个有趣的事情&#xff0c;链接是0221_韩顺平Java_老鼠出迷宫1_哔哩哔哩_bilibili 我们要找的是小老鼠按路径走到右下点 要点 我们这里方法调用时对于引用类型&#xff1a;如java中引用数据类型有哪些&#xff1f;_java引用数据类型-CSDN博客 会共享引用类型…

斯坦福大学的在线密码学课程

密码学是保护计算机系统信息不可或缺的工具。在本课程中&#xff0c;您将了解密码系统的内部工作原理&#xff0c;以及如何在实际应用中正确使用它们。课程首先将详细讨论当强大的对手窃听和篡改流量时&#xff0c;拥有共享密钥的双方如何进行安全通信。我们将研究许多已部署的…

部署Gerapy

1.Gerapy 是什么&#xff1f; Gerapy 是一款基于 Python 3 的分布式爬虫管理框架&#xff0c;它旨在简化和优化分布式爬虫的部署、管理和监控过程。 2.作用与功能&#xff1f; 2.1分布式管理&#xff1a; Gerapy 允许用户在多台机器上部署和管理Scrapy爬虫&#xff0c;实现爬虫…

【计算机毕设】小型企业办公自动化系统+vue - 免费源码(私信领取)

免费领取源码 &#xff5c; 项目完整可运行 &#xff5c; v&#xff1a;chengn7890 诚招源码校园代理&#xff01; 1. 研究目的 本项目旨在设计并实现一个小型企业办公自动化系统&#xff0c;利用Vue作为前端框架&#xff0c;为企业员工提供便捷的办公管理工具&#xff0c;提升…

基于51单片机的八路抢答器—加随机抽选功能

基于51单片机的八路抢答器 &#xff08;仿真&#xff0b;程序原理图&#xff0b;设计报告&#xff09; 功能介绍 具体功能&#xff1a; 1.主持人按键控制开始抢答&#xff1b; 2.开始抢答按下&#xff0c;数码管20秒倒计时&#xff1b; 3.8个按键代表八位选手&#xff0c;谁…

python面向函数

组织好的&#xff0c;可重复利用的&#xff0c;用来实现单一&#xff0c;或相关联功能的代码段&#xff0c;避免重复造轮子&#xff0c;增加程序复用性。 定义方法为def 函数名 (参数) 参数可动态传参&#xff0c;即使用*args代表元组形式**kwargs代表字典形式&#xff0c;代替…

tsconfig 备忘清单

前言 ❝ Nealyang/blog0 使用 ts 已多年&#xff0c;但是貌似对于 tsconfig 总是记忆不清&#xff0c;每次都是 cv 历史项目&#xff0c;所以写了这篇备忘录&#xff0c;希望能帮助到大家。 本文总结整理自 Matt Pocock 的一篇文章3&#xff0c;加以个人理解&#xff0c;并做了…
最新文章