题目描述

时间限制:1.2s 内存限制:256.0MB

问题描述

   如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。

输入格式

   第一行包含三个正整数\(N\),\(M\),\(S\),分别表示树的结点个数、询问的个数和树根结点的序号。

   接下来\(N-1\)行每行包含两个正整数\(x\),\(y\) ,表示\(x\)结点和\(y\)结点之间有一条直接连接的边(数据保证可以构成树)。

   接下来\(M\)行每行包含两个正整数\(a\),\(b\),表示询问\(a\)结点和\(b\)结点的最近公共祖先。

输出格式

   输出包含\(M\)行,每行包含一个正整数,依次为每一个询问的结果。

样例输入

 5 5 4
3 1
2 4
5 1
1 4
2 4
3 2
3 5
1 2
4 5

样例输出

4
4
1
4
4

数据规模和约定

   对于\(30%\)的数据,\(N<=10\),\(M<=10\)。

   对于\(70%\)的数据,\(N<=10000\),\(M<=10000\)。

   对于\(100%\)的数据,\(N<=500000\),\(M<=500000\)。

注意

   这道题里一个节点的祖先是包括自己的

解析

1.暴力(70分)

#include <iostream>
#include <cstring>
#include <vector> using namespace std; vector<int> a[500005];
int deep[500005], father[500005]; void dfs(int cur, int par) { // 用dfs遍历数
father[cur] = par; // 存父亲节点
deep[cur] = deep[par] + 1; // 存深度
for (int i = 0; i < a[cur].size(); i++)
if (a[cur][i] != par)
dfs(a[cur][i], cur);
} int find(int x, int y) { // 寻找两个节点的直接最近公共祖先
// 若两个节点的深度不同,先让两个节点深度相等,再一起向上寻找祖先
if (deep[x] < deep[y]) swap(x, y);
while (deep[x] > deep[y]) x = father[x];
while (x != y) x = father[x], y = father[y];
return x;
} int main(int argc, char** argv) {
int n, m, s, x, y; cin >> n >> m >> s;
for (int i = 0; i < n - 1; i++) {
cin >> x >> y;
a[x].push_back(y);
a[y].push_back(x);
}
dfs(s, 0);
for (int i = 0; i < m; i++) {
cin >> x >> y;
cout << find(x, y) << endl;
}
return 0;
}

2.优化

  首先我们发现主要影响速度的就是find函数

  可以分成两段,并齐深度和共同向上找这两个部分

并齐深度:

我们是知道x需要向上的次数,也就是deep[x] - deep[y]次

可以运用二进制的思想来减少询问次数

定义数组f[i][t]为i节点向上寻找2^t个的祖先

处理数组用递推的形式表示出来

f[i][0] = father[i];

f[i][j] = f[f[i][j - 1]][j - 1];

当然,会出现找到的祖先以超过根节点这种问题,可以将根节点s的祖先定位它本身

可将deep[x] - deep[y]转化为二进制形式,bin[n]若为1,表示向上询问对应的次数

向上寻找祖先:

t从19到0

每次询问x与y向上2^t的公共祖先是否相同

若不相同时退出循环x的父节点则是x与y的最近公共祖先
#include <iostream>
#include <cstring>
#include <vector> using namespace std; vector<int> a[500005];
int deep[500005], father[500005];
int f[500005][25];
int n, s; void dfs(int cur, int par) {
father[cur] = par;
deep[cur] = deep[par] + 1;
for (int i = 0; i < a[cur].size(); i++)
if (a[cur][i] != par)
dfs(a[cur][i], cur);
} void init() {
for (int i = 1; i <= n; i++)
f[i][0] = father[i];
for (int j = 1; j <= 19; j++)
for (int i = 1; i <= n; i++)
f[i][j] = f[f[i][j - 1]][j - 1];
} int find(int x, int y) {
if (deep[x] < deep[y]) swap(x, y);
int w = 0, N = deep[x] - deep[y];
while (N) {
if (N % 2 == 1) x = f[x][w];
N /= 2;
w++;
}
if (x == y) return x;
for (int t = 19; t >= 0; t--){
if (f[x][t] != f[y][t])
x = f[x][t], y = f[y][t];
}
return father[x];
} int main(int argc, char** argv) {
ios::sync_with_stdio(false);
int m, x, y; cin >> n >> m >> s;
for (int i = 0; i < n - 1; i++) {
cin >> x >> y;
a[x].push_back(y);
a[y].push_back(x);
}
dfs(s, s); init();
for (int i = 0; i < m; i++) {
cin >> x >> y;
cout << find(x, y) << endl;
}
return 0;
}

最近公共祖先-LCA的更多相关文章

  1. Luogu 2245 星际导航(最小生成树,最近公共祖先LCA,并查集)

    Luogu 2245 星际导航(最小生成树,最近公共祖先LCA,并查集) Description sideman做好了回到Gliese 星球的硬件准备,但是sideman的导航系统还没有完全设计好.为 ...

  2. POJ 1470 Closest Common Ancestors(最近公共祖先 LCA)

    POJ 1470 Closest Common Ancestors(最近公共祖先 LCA) Description Write a program that takes as input a root ...

  3. POJ 1330 Nearest Common Ancestors / UVALive 2525 Nearest Common Ancestors (最近公共祖先LCA)

    POJ 1330 Nearest Common Ancestors / UVALive 2525 Nearest Common Ancestors (最近公共祖先LCA) Description A ...

  4. [模板] 最近公共祖先/lca

    简介 最近公共祖先 \(lca(a,b)\) 指的是a到根的路径和b到n的路径的深度最大的公共点. 定理. 以 \(r\) 为根的树上的路径 \((a,b) = (r,a) + (r,b) - 2 * ...

  5. 【lhyaaa】最近公共祖先LCA——倍增!!!

    高级的算法——倍增!!! 根据LCA的定义,我们可以知道假如有两个节点x和y,则LCA(x,y)是 x 到根的路 径与 y 到根的路径的交汇点,同时也是 x 和 y 之间所有路径中深度最小的节 点,所 ...

  6. POJ 1470 Closest Common Ancestors (最近公共祖先LCA 的离线算法Tarjan)

    Tarjan算法的详细介绍,请戳: http://www.cnblogs.com/chenxiwenruo/p/3529533.html #include <iostream> #incl ...

  7. 【Leetcode】查找二叉树中任意结点的最近公共祖先(LCA问题)

    寻找最近公共祖先,示例如下: 1 /           \ 2           3 /    \        /    \ 4    5      6    7 /    \          ...

  8. 最近公共祖先LCA(Tarjan算法)的思考和算法实现

    LCA 最近公共祖先 Tarjan(离线)算法的基本思路及其算法实现 小广告:METO CODE 安溪一中信息学在线评测系统(OJ) //由于这是第一篇博客..有点瑕疵...比如我把false写成了f ...

  9. 查找最近公共祖先(LCA)

    一.问题 求有根树的任意两个节点的最近公共祖先(一般来说都是指二叉树).最近公共祖先简称LCA(Lowest Common Ancestor).例如,如下图一棵普通的二叉树. 结点3和结点4的最近公共 ...

  10. 最近公共祖先(LCA)的三种求解方法

    转载来自:https://blog.andrewei.info/2015/10/08/e6-9c-80-e8-bf-91-e5-85-ac-e5-85-b1-e7-a5-96-e5-85-88lca- ...

随机推荐

  1. Python调用Prometheus监控数据并计算

    Prometheus是什么 Prometheus是一套开源监控系统和告警为一体,由go语言(golang)开发,是监控+报警+时间序列数 据库的组合.适合监控docker容器.因为kubernetes ...

  2. Arm64架构下编译便携Python

    这段时间,我一直忙于将 Rainbond 源码构建模块移植到 Arm64/aarch64 架构中.对于 Python 项目而言,可以直接通过源代码编译成为可运行在各种容器平台之上的容器镜像.这个过程不 ...

  3. Ubuntu复习笔记-认识Linux

    本次复习基于\(Ubuntu20.04\)的发行版进行总结,目的是更好记录自己学习的\(Linux\). 认识Linux 学习\(Linux\)之前,需要搞懂几个概念,\(Linux\)桌面操作系统与 ...

  4. 云南农业职业技术学院 - 互联网技术学院 - 美和易思《MYSQL 高级查询与编程》 综合机试试卷

    数据库及试题文档下载:https://download.csdn.net/download/weixin_44893902/14503097 目录 题目:电商平台 mysql 数据库系统管理 一. 语 ...

  5. Drools的Eclipse_IDEA插件安装

    1.说明 Drools使用时不是必须依赖插件的, 只是在安装了相应的插件之后, 在开发工具中能识别到对应的drools文件, 能够进行一些智能提示. 以及使用插件提供的便捷功能. 2.Eclipse插 ...

  6. 深入 Laravel 内核之 PHP 反射机制和依赖注入

    结论: PHP中提供了反射类来解析类的结构: 通过反射类可以获取到类的构造函数及其参数和依赖: 给构造函数的参数递归设置默认值后,即可使用这些带默认值的参数通过 newInstanceArgs 实例化 ...

  7. VirtualBox虚拟机安装win8/10

    你可能会遇到过,需要win8来做一些操作,不过自己的本机是win7,难道要重装系统吗?操作好了后,想用回win7怎么办?这个时候,如果旁边有人的系统刚好符合你对系统的要求,那可以借用,如果使用时间太长 ...

  8. 初识python: random 模块

    random 顾名思义,就是取 随机数,需要导入random模块. import random 1.随机获取一个0到1之间的小数(不含首尾) print(random.random()) 2.随机获取 ...

  9. slf4j+logback日志框架 的具体使用操作【spring boot自带的默认日志框架】

    1.前言 是不是还在使用System.out.println()打印数据到控制台看? 东西少还好,如果多起来,那就看的很烦人了,特别还有加时间等信息. 怎么解决? 可以使用日志框架 ,常见的有 log ...

  10. spring security 动态 修改当前登录用户的 权限

    1.前言 spring security 可以获取当前登录的用户信息,同时提供了接口 来修改权限列表信息 , 使用这个方法 ,可以动态的修改当前登录用户权限. 那么问题来了... 如果我是管理员 ,如 ...