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

Calibre 3Dstack Flow Example(5-2) 本文详细介绍了Calibre3DSTACK工具在3D-IC设计验证中的应用。主要内容包括:1)连通性检查(LVS)规则编写,重点说明connected命令的使用方法和多文本标签处理;2)设计规则检查(DRC)实现,涵盖层间距检查和焊盘重叠检查;3)系统网表生成器操作流程,从芯片导入到网连通性定义;4)两种规则文件语法(扩展语法和传统语法)的编写示例;5)验证报告文件格式解析。通过具体案例演示了如何建立完整的3D-IC验证流程,包括芯片定义、装配操作、电气连接验证和物理规则检查等关键步骤,为3D-IC设计验证 阅读详情

二叉树中的最低公共祖先

题目描述

树中两个结点 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;
}
如何通过conda安装HyPhy教程 本文介绍了在Ubuntu系统上通过conda安装分子进化分析工具HyPhy的详细步骤。首先创建conda环境并指定Python 3.8版本,然后通过bioconda通道安装HyPhy软件包。安装完成后可通过conda list命令验证安装。文中还特别指出可能遇到的libcrypto.so.1.1共享库缺失错误,并提供了解决方案的参考链接。该教程配有安装过程截图,帮助用户直观了解操作流程和验证安装结果。 阅读详情

相关推荐

Python介绍与安装

Python介绍与安装

acegi13579的专栏 3221

深挖C++:类和对象(中)

(先进后出)

xys_678的博客 246

别再手动填数据了!Vivado里用.coe文件给ROM IP核预装数据的保姆级教程

本文详细介绍了在Vivado中使用.coe文件为ROM IP核预装数据的全流程教程,包括文件格式规范、数据转换技巧和Vivado配置指南。通过自动化数据加载,开发者可以显著提升FPGA开发效率,避免手动输入错误,适用于查找表、波形存储等多种应用场景。

weixin_33377602的博客 338

C++移动构造函数详解

移动构造函数是C++11引入的一种特殊构造函数,用于从临时对象(右值)"窃取"资源来初始化新对象,而不是进行昂贵的深拷贝。char* data;public:// 移动构造函数:参数是右值引用 &&// 关键:把源对象的资源置空。

拒绝AI文,努力在AI时代写出感动人心的好文章 392

Adams软件安装与配置全攻略:从入门到机器人仿真

机械系统动力学仿真软件Adams是工业机器人开发的重要工具,其安装过程涉及许可证配置、环境变量设置等关键技术环节。专业仿真软件安装通常需要处理系统兼容性、组件依赖和授权验证等工程问题,特别是在机器人控制仿真等场景中,还需集成MATLAB/Simulink等第三方工具。本文以Windows平台为例,详解Adams安装的核心步骤,包括系统环境检查、许可证服务器配置、工业机器人专项模块安装等实践要点,并针对NVIDIA Quadro显卡优化、27500端口通信等常见问题提供解决方案,帮助用户快速搭建可靠的仿真环境

weixin_28523493的博客 199

电机驱动技术中关于磁阻转矩生成的理解

正交在物理学和数学中指的是两个方向或两个向量之间的夹角为90°(直角)。在图中,当气隙磁场的轴线与转子轴线的夹角为90°时,我们称它们为正交。转子磁极的方向。通常由转子的磁极中心指向另一极的连线表示。转子轴线用图示中的d 轴表示。通过对定子、转子、气隙磁场、磁力线的理解,我们可以清晰地分析不同角度下电磁转矩的方向。这种分析不仅有助于电机的理论学习,还能为实际设计提供重要参考。掌握这些基础知识后,可以更好地理解电机工作原理及优化其性能。

weixin_48327795的博客 2074

ThunderKittens 深度解析:一个不基于 MLIR 的 C++ 模板 GPU DSL

Triton 是"让更多人能写 GPU kernel",ThunderKittens 是"让会写的人写得更爽"。前者用 MLIR 换易用性,后者用 C++ 模板换可控性。它们不是替代关系,而是覆盖了 GPU 编程光谱的两端。ThunderKittens 的出现,本质上是对"GPU DSL 必须基于 MLIR"这一默认假设的挑战。编译期类型系统本身就可以是一个强大的 DSL 基础设施,不需要中间 IR,不需要黑盒 pass,也能实现 tile 级抽象和零运行时开销。

Maxwell_Newton的博客 343

工业4.0必备:C#上位机+数字孪生 生产线实时监控与智能预警系统(工业级实战)

数据割裂:PLC、传感器、工业相机的数据散落在不同系统,想看全局生产状态要切换N个软件,“数据孤岛”导致决策滞后;故障难预判:设备突发故障时,只能停机排查,平均故障响应时间超30分钟,一条生产线停工1小时损失超10万;可视化差:传统监控只有冰冷的数字和2D图表,设备运行状态、生产流程、物料流转无法直观感知,新人上手需3个月才能熟悉产线。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 660

跟我学c++中级篇—类型定义的方式

本文系统阐述了编程中类型定义的重要性及c++中的实现方式。重点对比了宏定义、typedef与modern using三种机制,指出using在可读性、灵活性和模板支持上的优势,推荐优先使用。同时提醒开发者注意别名滥用、类型歧义、错误信息冗长等问题,强调统一规范与合理设计。最终结论:在现代c++开发中,应以using为主,兼顾简洁性与可维护性,避免陷入命名混乱与编译困境。

fpcc的专栏 82

【专栏必读】王道考研408数据结构+算法设计与分析万字笔记使用说明及章节导航

各位老铁思维导图在我整理完笔记后会放上去的,我需要根据王道的思维导图重新再做一遍 文章目录一:有关注意事项二:关于专栏三:学习建议四:各专栏导航(各位老铁别催,质量优先)(1)第一章:绪论(2)第二章:线性表(3)第三章:栈和队列第四章:串第五章:二叉树第六章:图 首先感谢王道大大(手动比心),很用心在做了,大家可以看其中两节是否符合胃口 (王道408考研数据结构)第五章树-第三节1:二叉树遍历(先序、中序和后序) (王道408考研数据结构)第五章树-第四节2:平衡二叉树(AVL)及其旋转 一:有.

快乐江湖的博客 5万+

关于 SAP UI5 应用 url 中的特殊符号 # 的作用介绍

符号在 SAP UI5 的 URL 中不仅仅是一个分隔符,更是动态路由、状态管理和深链接的关键所在。通过合理使用,开发者可以实现高效、流畅的用户体验,同时保持系统的可维护性和扩展性。通过 SAP UI5 提供的灵活路由机制,企业应用可以轻松实现复杂导航需求,同时为用户提供直观的交互体验。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 412

【C语言】字符与字符串函数

C语言中字符串为以\0结尾的字符数组,标准库函数基于此约定。本文解析字符串内存布局,详解ctype.h与string.h中字符分类、长度计算、拷贝、比较等函数的使用要点,强调长度判断与越界风险,最后通过命令行待办事项示例整合fgets、sscanf、strncpy、strcmp,展示实际应用中的关键细节。

记录在学习过程中学到的知识 352

RK3568 RGA硬件加速详解:图像缩放、旋转、格式转换10倍提速

RGA(Raster Graphics Acceleration)是瑞芯微(Rockchip)芯片系列中的 2D 图形加速引擎,专门用于处理像素级的图像操作。它通过硬件实现常见的图像处理算法,解放 CPU,降低功耗。RGA 是 RK3568 平台上一颗强大的 2D 图形加速引擎,能够:✅10-20 倍性能提升:大幅减少图像处理时间✅降低 CPU 占用:释放 CPU 资源给其他任务✅丰富的功能:缩放、旋转、裁剪、格式转换一应俱全✅易于集成:librga 提供简洁的 C API。

小灰灰的博客 1696

C++ 函数模版

模板参数:● template ,T 就是类型占位符,名字可以自定义,习惯用T● 占位符可以在函数返回值、形参、函数内部当作类型使用使用要点模板代码一般写在头文件:编译器需要看到完整模板源码,才能在每个编译单元完成实例化全局函数、类内成员函数都可以写成模板实例化发生在编译阶段,不是运行时隐式实例化:调用时编译器自动推导实参类型,生成对应版本函数。

2501_94041724的博客 305

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

C++ 运行时多态与虚函数机制全面剖析【面试重点】

构成C++ 运行时多态必须是基类的指针或者引用调用虚函数。被调用的函数必须是虚函数,并且派生类必须对该虚函数完成重写/覆盖(Override)。只有基类的指针或引用才能既指向基类对象,又指向派生类对象(切片机制);而只有完成了虚函数重写,派生类与基类才具备不同的函数行为,达到“指向谁就调用谁”的效果。在类成员函数的前面加上virtual关键字修饰,该成员函数就成为了虚函数。public:// 成员函数前加 virtual 修饰,声明为虚函数。

Fuyo_0209的博客 439

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

#SQLite 数据库笔记一:(概念 / 终端指令 / SQL 语句 / C 编程 / 回调函数)

数据库 = 数据的仓库,假设数据是海量。能力说明存储数据持久化保存,程序退出数据还在统计数据、分组、排序数据管理增、删、改、查(CRUD)嵌入式设备上也会用数据库(单片机网关、路由器、工业 HMI 都常见),因为 sqlite 体积小、无需服务进程、单文件即可。数据库 = 库 > 表 > 记录(行) > 字段(列)sqlite3 = 开源、C 写的、单文件、免安装、最多 2T 的嵌入式关系型数据库。

lwq20051110的博客 217

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

C 语言 malloc() 详解与实战 Demo

本文详解C语言malloc()函数,涵盖其语法、内存原理及实战应用。通过动态整数、数组、结构体与链表的Demo,演示如何安全申请、使用与释放内存,强调避免内存泄漏与空指针检查的重要性,帮助开发者掌握动态内存管理核心技能。

tangPHP的博客 374

蓝桥杯·全国青少年信息素养大赛·电子学会Python/C++考级 备考:莫队算法离线区间查询详解

备赛进行时:第十八届蓝桥杯软件赛报名已于 2026-10-08 10:00 开启、12-18 14:00 截止,省赛定在 2027-04-10(C/C++·Python 程序设计 9:00–13:00);中国电子学会 2026 年 12 月软件编程(C/C++ 一级至十级、Python 一级至六级)考级报名 9/23–12/2 16:00、统考 12-12;全国青少年信息素养大赛 2026—2027 赛季算法应用主题赛也已进入复盘练兵窗口。

IT_Scratch的博客 156

【C++ 基础入门】类和对象(上):封装、对象模型与 this 指针

C++类与对象知识(面向过程和面向对象的初步认识,类的初步认识,类的访问限定符及封装,类的作用域、实例化,类的对象模型,this指针)

2601_96587588的博客 887

【C++ 基础入门】C++继承(下):问题、底层原理与组合

C++继承(菱形继承的问题、虚继承原理、继承与组合、构造顺序)

2601_96587588的博客 795

C++09_vector类复习

本文以10个核心问题为主线,系统解析C++ vector的底层机制:从基本概念、初始化方式、遍历方法,到size与capacity的区别、扩容策略(VS 1.5×,g++ 2×)、reserve与resize差异、中间增删的挪动原理、迭代器失效原因,直至二维vector的内存布局(含三指针结构)。通过“先会用、再懂扩容、最后看清对象结构”的递进逻辑,揭示vector作为动态数组的本质——连续内存、自动扩容、随机访问高效,但插入删除代价高,迭代器易失效。全文深入浅出,助读者摆脱“黑盒”认知,真正掌握其内存图景

lemo_n_的博客 338

825 mW 顶上 212 °C:ASM491S 的 105 °C/W、250 mA 短路电流与热关断

25 °C 环境里结温 34.1 °C,125 °C 环境里结温 134.1 °C——后者已经离 150 °C 只有 16 °C 了,但还在范围内。换个角度看同一件事:从 25 °C 和从 125 °C 起算,温升都是 86.6 °C,但一个到 111.6 °C(还在范围内),一个到 211.6 °C(远远超限)。这个算例里 20 °C/W 是我假设的,实际值取决于机壳材料、密封程度、有没有风扇,跨度可以从 5 °C/W 到 50 °C/W 以上。外面 50 °C 的机箱,盒内就是 63.9 °C。

ANSILIC的博客 358

《C++ 项目:手写大模型接入 SDK》Gemini接口设计(上):初始化、全量响应实现和测试

本文系统完成了Gemini模型通过OpenAI兼容API的接入开发,基于Apifox验证接口规范,设计并实现GeminiProvider类,涵盖初始化、状态检测、全量响应等核心功能。重点处理了代理配置强制校验、Bearer鉴权、JSON序列化与响应解析等工程细节,并通过GTest与CMake完成单元测试,验证链路可用性。项目遵循策略模式,保持与DeepSeek、ChatGPT等模型的统一抽象接口,显著提升SDK可扩展性。

2501_91275995的博客 392

头歌 数据结构——C&C++线性表实训

本系列任务围绕线性表的链式实现,依次完成顺序构建、逆序构建、排序插入、查找、删除、求长度及栈、队列、集合等应用。通过动态内存分配与指针操作,掌握单链表的基本操作与实际应用,涵盖插入、删除、遍历、搜索等核心功能,最终实现数据结构在典型场景中的灵活运用。

一名敲代码的苦13程序员 239

C++ vector 添加与删除元素的底层原理:erase 返回值与倒序遍历删除

vector是连续内存的动态数组,支持O(1)随机访问,但扩容时需重新分配内存并搬迁元素。push_back和emplace_back均摊时间复杂度为O(1),扩容策略(如2倍或1.5倍)确保总代价均摊;insert与erase在中间位置操作需移动元素,时间复杂度O(n),且使后续迭代器失效。删除指定元素推荐使用erase-remove惯用法或std::erase_if,避免逐个删除导致O(n²)复杂度。从后往前遍历可防止漏删,但最优解仍是高效惯用法。预估容量后调用reserve()可避免频繁扩容,提升性能

2402_85743155的博客 797

字母移动游戏题解:贪心求最长可保留连续子串(附 C 实现)

本题核心思想是:最少移动步数 = 总长度 - 可保留的最长连续子串长度。未移动字母在T中必须为连续子串,在S中需保持相对顺序(即子序列)。通过枚举T的所有连续子串起点,贪心匹配S中的对应字符,求出最长可保留长度L,答案即为n−L。时间复杂度O(n²),空间复杂度O(n),适用于n≤1000。

冻柠茶飞冰走茶 588
上一篇: 洛谷 P3379 [模板] 最近公共祖先(LCA)
下一篇: Codeforces Round 936 (Div. 2) C. Tree Cutting
bbc_ouo
博客等级 码龄3年 2096粉丝 132原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值