题目描述

时间限制: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. @Transactional 注解实现

    @Transactional注解简介 @Transactional是spring中声明式事务管理的注解配置方式,相信这个注解的作用大家都很清楚.@Transactional注解可以帮助我们把事务开启. ...

  2. [C++]C++ STL库函数大全

    #include <assert.h> //设定插入点 #include <ctype.h> //字符处理 #include <errno.h> //定义错误码 # ...

  3. 【VUE】VUE使用常见问题搜集

    文章目录 1.日期格式化 2.引用JSON文件中的数据 1.日期格式化 安装插件,官网:Moment.js npm install moment --save 示例: //Sat Mar 14 202 ...

  4. Linux无法登陆,var目录权限修改导致SSH失败

    1.问题说明 Linux远程服务器突然无法SSH登录了, 登陆报错: ssh_exchange_identification: read: Connection reset by peer. 2.问题 ...

  5. Hadoop 之 高可用不自动切换(ssh密钥无效 Caused by: com.jcraft.jsch.JSchException: invalid privatekey )

    案例 在安装hadoop ha之后,验证HDFS高可用时,怎么都不能实现自动切换.查看zkfc日志发现错误信息如下: WARN org.apache.hadoop.ha.SshFenceByTcpPo ...

  6. Selenium_使用Select类对象处理下拉框(15)

    select标签的下拉框可以使用selenium的 Select模拟下拉框选择操作. Select需要导入才能使用,导入路径如下 from selenium.webdriver.support.ui ...

  7. Eclipse导包

    导包快捷键:"Ctrl+Shift+M",但是一般不用,一般利用整理包的快捷键. 整理包的快捷键:"Ctrl+Shift+O",与导包的区别在于,有用的留着,没 ...

  8. js 关于replace() 的使用心得

    1.前言 我想把一段话 let a = "抱歉,您当前的主治医生有紧急情况不得不下班,您的预约将由<br>医生:里斯<br>为您就诊,<br>诊室位置:门 ...

  9. docker安装easymock

    一.准备 参考docker安装mongodb与redis文章 二.拉取 docker pull docker.io/easymock/easymock 三.启动 1.创建配置目录 mkdir -f / ...

  10. SYCOJ304末尾0的个数

    https://oj.shiyancang.cn/Problem/304.html 首先数据范围不可能算出来的,那么就要看数的性质. 0是怎么来的首先我们知道,有一个0,就必然会有一个5和2. n!在 ...