题目描述

时间限制: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. 【转】GitHub 的 Pull Request 和 GitLab 的 Merge Request 有区别吗?

    在 GitHub 上混久了,经常听到 Pull Request,在 GitLab 上混久了,则经常 提起 Merge Request ,然而它们之间有不同吗?为什么要用两个不同的名称? 要追溯这两个名 ...

  2. uniCloud爬虫获取网页数据

    'use strict'; let request = require('request') let cheerio = require('cheerio'); //爬虫 let iconv = re ...

  3. JS中常见的几种控制台台报错

    Error 控制台报错 EvalError 全局错误RangeError 引用错ReferenceError 参数错误SyntaxError 语法错误TypeError 类型错误URIError 编码 ...

  4. Java初学者作业——编写Java程序,简单判断“王者荣耀”英雄收到攻击后是否死亡

    返回本章节 返回作业目录 需求说明: 判断"王者荣耀"中英雄受到攻击后是否死亡? 计算"王者荣耀"中怪物攻击英雄的伤害,做出英雄死亡的判断. 如果英雄受到过量伤 ...

  5. 编写Java程序,创建一个 XML 文档,文档名为“hero.xml”,用于保存“王者荣耀”的英雄信息。

    查看本章节 查看作业目录 需求说明: 创建一个 XML 文档,文档名为"hero.xml",用于保存"王者荣耀"的英雄信息.英雄信息包括编号(id).姓名(na ...

  6. CGO封装C语言qsort函数

    封装qsort函数 package qsort /* #include <stdlib.h> typedef int (*qsort_cmp_func_t) (const void* a, ...

  7. js 图片跟随鼠标移动效果 案例

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  8. python日志按天分割,保存近一个月日志,日志自动清理

    python日志按天分割,保存近一个月日志 import os import logging import re from logging.handlers import TimedRotatingF ...

  9. Python_jsonPath模块的使用

    jsonpath简介 如果有一个多层嵌套的复杂字典,想要根据key批量提取value,还是比较繁琐的.jsonPath模块就能解决这个痛点,接下来我们来学习一下jsonpath模块. 因为jsonpa ...

  10. Launch agent by connecting it to the master

    Jenkins Node 是 Windows, Jenkins Server 在 Linux C:\JenkinsAgent\start_jenkins_agent.bat java -DSoftKi ...