题目描述

时间限制: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. 使用.NET 6开发TodoList应用(10)——实现DELETE请求以及HTTP请求幂等性

    系列导航及源代码 使用.NET 6开发TodoList应用文章索引 需求 先说明一下关于原本想要去更新的PATCH请求的文章,从目前试验的情况来看,如果是按照.NET 6的项目结构(即只使用一个Pro ...

  2. 「Codeforces 468C」Hack it!

    Description 定义 \(f(x)\) 表示 \(x\) 的各个数位之和.现在要求 \(\sum_{i=l}^rf(i)\bmod a\). 显然 ans=solve(l,r)%a; if(a ...

  3. CLION 使用自己的makefile来运行

    之前参考过这里和这里,都是说要使用add_custom_target,无奈看不懂 但是前一篇里说它参考的是stackoverflow上的回答,去原帖里发现后来更新的第二高票答案!!!非常好用!!! 在 ...

  4. JavaScript8种数据类型

    一.开门见山 在ES5的时候,我们认知的数据类型确实是 6种:Number.String.Boolean.undefined.object.Null. ES6 中新增了一种 Symbol .这种类型的 ...

  5. PL2586旺玖|USB 2.0HUB 工业级芯片|PROLIFIC PL2586

    工业级  USB 2.0 HUB 高速4端口集线器控制器 PL2586 1.PL2586说明   PL2586是USB 2.0高速4端口集线器控制器的高性能解决方案,完全符合通用串行总线规范2.0.控 ...

  6. Java实习生常规技术面试题每日十题Java基础(一)

    目录 1.Java 的 "一次编写,处处运行"如何实现? 2.描述JVM运行原理. 3.为什么Java没有全局变量? 4.说明一下public static void main(S ...

  7. vue3+TypeScript+vue-router使用

    简单使用 创建项目 vue-cli创建 $npm install -g @vue/cli $vue --version @vue/cli 4.5.15 $vue create my-project 然 ...

  8. Eclipse控制台Console使用说明

    1.说明 本文详细介绍Eclipse控制台Console使用说明, 调试时通过控制台查看日志, 有时候日志太多会找不到上面的日志, 有时候几个控制台会不受控制的弹出, 那么请参考本文, 通过调整Ecl ...

  9. SpringBoot集成MyBatis-Plus框架

    1.说明 本文介绍Spring Boot集成MyBatis-Plus框架, 重点介绍需要注意的地方, 是SpringBoot集成MyBatis-Plus框架详细方法 这篇文章的脱水版, 主要是三个步骤 ...

  10. NoSQL是什么(一)

    最常见的数据库可以分为下面的两种类型: RDBMS(关系型数据库):常见的关系型数据库有 Oracle.DB2.Microsoft SQL Server.Microsoft Access.MySQL: ...