Luogu3379 【模板】最近公共祖先(LCA)
题面
题解
这里讲一种硬核做法。
首先\(\mathrm{dfs}\)整棵树,求出这棵树的欧拉序,然后\(\mathrm{LCA}\)问题就变成了\(\pm 1\mathrm{RMQ}\)问题。
考虑\(\mathrm{O}(n)\)解决\(\pm 1\mathrm{RMQ}\)问题。
将原序列分块,每一块长度为\(\dfrac {\log_2 n}2\),块外用\(\mathrm{ST}\)表预处理,复杂度\(\mathrm{O}(n)\),考虑块内如何\(\mathrm{O}(1)\)回答。
因为相邻两项之差最多为\(1\),所以块内本质不同的状态只有\(2 ^ {\frac {\log n} 2} = \sqrt n\)种。
那么可以设\(f[S][l][r]\)表示状态为\(S\)时,区间\([l, r]\)的最小值。
于是块内就能\(\mathrm{O}(1)\)解决了,这一部分预处理的复杂度为\(\mathrm{O}(\sqrt n \log^2n)\)。
因为以上操作复杂度均没有超过\(\mathrm{O}(n)\),所以预处理的复杂度为\(\mathrm{O}(n)\),总复杂度为\(\mathrm{O}(n) - \mathrm{O}(1)\)。
代码
#include <cstdio>
#include <cmath>
#include <algorithm>
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
inline int read()
{
int data = 0, w = 1; char ch = getchar();
while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
if (ch == '-') w = -1, ch = getchar();
while (ch >= '0' && ch <= '9') data = data * 10 + (ch ^ 48), ch = getchar();
return data * w;
}
const int maxn(500010), LEN(1050), BLK(10), N(200010);
struct edge { int next, to; } e[maxn << 1];
int head[maxn], e_num, n, m, S, dep[maxn], f[1 << BLK][BLK][BLK];
int A[maxn << 1], ST[BLK << 1][N], Log[maxn << 1], pos[maxn], cnt;
int Len, Blk, minv[N], set[N], B[maxn << 1];
inline int min(const int &x, const int &y) { return B[x] < B[y] ? x : y; }
inline int Pos(const int &x) { return (x - 1) / Len + 1; }
inline int Posy(const int &x) { return (x - 1) % Len; }
inline void add_edge(int from, int to)
{
e[++e_num] = (edge) {head[from], to};
head[from] = e_num;
}
void dfs(int x, int fa)
{
A[pos[x] = ++cnt] = x, B[cnt] = dep[x];
for (int i = head[x]; i; i = e[i].next)
{
int to = e[i].to; if (to == fa) continue;
dep[to] = dep[x] + 1, dfs(to, x), A[++cnt] = x, B[cnt] = dep[x];
}
}
void Init()
{
Len = std::max(1, (int) (log(cnt * 1.) / log(2.) * .5));
Blk = cnt / Len + (cnt % Len > 0); int SIZ = 1 << (Len - 1);
for (int i = 2; i <= cnt; i++) Log[i] = Log[i >> 1] + 1;
for (int i = 0; i < SIZ; i++) for (int l = 0; l < Len; l++)
for (int r = (f[i][l][l] = l) + 1, now = 0, _min = 0; r < Len; r++)
{
f[i][l][r] = f[i][l][r - 1];
if (i & (1 << (r - 1))) ++now;
else { --now; if(now < _min) _min = now, f[i][l][r] = r; }
}
for (int i = 1; i <= cnt; i++)
if (!Posy(i)) minv[Pos(i)] = i, set[Pos(i)] = 0;
else
{
if (B[i] < B[minv[Pos(i)]]) minv[Pos(i)] = i;
if (B[i] > B[i - 1]) set[Pos(i)] |= 1 << (Posy(i) - 1);
}
for (int i = 1; i <= Blk; i++) ST[0][i] = minv[i];
for (int i = 1; i <= Log[Blk]; i++)
for (int j = 1; j <= Blk - (1 << i) + 1; j++)
ST[i][j] = min(ST[i - 1][j], ST[i - 1][j + (1 << (i - 1))]);
}
int Query(int l, int r)
{
l = pos[l], r = pos[r]; if(l > r) std::swap(l, r);
int idl = Pos(l), idr = Pos(r);
if (idl == idr) return (idl - 1) * Len + f[set[idl]][Posy(l)][Posy(r)] + 1;
else
{
int a1 = (idl - 1) * Len + f[set[idl]][Posy(l)][Len - 1] + 1;
int a2 = (idr - 1) * Len + f[set[idr]][0][Posy(r)] + 1;
int ans = min(a1, a2), _l = Log[idr - idl - 1];
if (idr - idl - 1)
return min(ans, min(ST[_l][idl + 1], ST[_l][idr - (1 << _l)]));
return ans;
}
}
int main()
{
n = read(), m = read(), S = read();
for (int i = 1, a, b; i < n; i++)
a = read(), b = read(), add_edge(a, b), add_edge(b, a);
dep[S] = 1, dfs(S, 0); Init();
for (int a, b; m--; ) a = read(), b = read(), printf("%d\n", A[Query(a, b)]);
return 0;
}
Luogu3379 【模板】最近公共祖先(LCA)的更多相关文章
- [模板] 最近公共祖先/lca
简介 最近公共祖先 \(lca(a,b)\) 指的是a到根的路径和b到n的路径的深度最大的公共点. 定理. 以 \(r\) 为根的树上的路径 \((a,b) = (r,a) + (r,b) - 2 * ...
- Luogu 2245 星际导航(最小生成树,最近公共祖先LCA,并查集)
Luogu 2245 星际导航(最小生成树,最近公共祖先LCA,并查集) Description sideman做好了回到Gliese 星球的硬件准备,但是sideman的导航系统还没有完全设计好.为 ...
- POJ 1470 Closest Common Ancestors(最近公共祖先 LCA)
POJ 1470 Closest Common Ancestors(最近公共祖先 LCA) Description Write a program that takes as input a root ...
- POJ 1330 Nearest Common Ancestors / UVALive 2525 Nearest Common Ancestors (最近公共祖先LCA)
POJ 1330 Nearest Common Ancestors / UVALive 2525 Nearest Common Ancestors (最近公共祖先LCA) Description A ...
- 【lhyaaa】最近公共祖先LCA——倍增!!!
高级的算法——倍增!!! 根据LCA的定义,我们可以知道假如有两个节点x和y,则LCA(x,y)是 x 到根的路 径与 y 到根的路径的交汇点,同时也是 x 和 y 之间所有路径中深度最小的节 点,所 ...
- luogu3379 【模板】最近公共祖先(LCA) 倍增法
题目大意:给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 整体步骤:1.使两个点深度相同:2.使两个点相同. 这两个步骤都可用倍增法进行优化.定义每个节点的Elder[i]为该节点的2^k( ...
- 最近公共祖先(LCA)模板
以下转自:https://www.cnblogs.com/JVxie/p/4854719.html 首先是最近公共祖先的概念(什么是最近公共祖先?): 在一棵没有环的树上,每个节点肯定有其父亲节点和祖 ...
- HDU 2586 How far away ?(LCA模板 近期公共祖先啊)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2586 Problem Description There are n houses in the vi ...
- 最近公共祖先lca模板
void dfs(int x,int root){//预处理fa和dep数组 fa[x][0]=root; dep[x]=dep[root]+1; for(int i=1;(1<<i)&l ...
- 【洛谷 p3379】模板-最近公共祖先(图论--倍增算法求LCA)
题目:给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 解法:倍增. 1 #include<cstdio> 2 #include<cstdlib> 3 #include ...
随机推荐
- 2-python元组和列表
目录 元组 列表 1.元组 - 元素有序排列 - 一个元组中的元素不需要具有相同的类型 - 元素不可增添.修改和删除 1.1.创建元组 # 创建元组 tup1 = (1,2,3,4) tup2 = t ...
- SpringBoot之多Profile配置
近来在利用闲暇时间巩固下SpringBoot的基本知识,然后自己也做一些笔记,整理下当时所学知识,后面就干脆写到这里来了. 多Profile配置文件 在SpringBoot主配置文件编写的时候,文件名 ...
- 浏览器提示:源映射错误:request failed with status 404 源 URL:http://xxx.js 源映射 URL:jquery.min.map
浏览器 jquery1.9.1min.js 报脚本错误 无jquery.min.map 文件 最近在浏览个人网站的时候就遇到了这个问题 我先说一下什么是source map文件. source map ...
- 《区块链DAPP开发入门、代码实现、场景应用》笔记5——区块链福利彩票的设计
笔者一直强调,一定要利用区块链的特点来解决行业存在的问题,并且该问题最好用区块链解决或者说只能用区块链解决.彩票行业就是个例子. 在讲解代码之前,首先讲解一下业务设计,如图6.15所示. 图6.15 ...
- Oracle数据库之查询
一.单表简单查询: 1. select * from scott.emp 2.去重: --去除重复记录 select distinct ssex from java0322; select disti ...
- jq对象才能使用jq方法,$(".a").eq(0) 和 $(”.a“)[0]
<a class="a"></a> <a class="a"></a> <a class="a& ...
- css-博客样式初体验
css学习一周后,写了个基础博客样式. 样式是出来了,但是在写的过程中感觉css写的杂乱无章,可能是写的太少了吧,条例不是很清除,只是在写的过程 中一个点一个点的套,感觉样式出来即可,没有做到由全局出 ...
- 水泥caement单词
Caement英语单词,翻译为:水泥 中文名:水泥 外文名:caement 目录 释义 caement 读音:英 [sɪˈment] 美 [sɪˈmɛnt] Noun名词. 水泥; caement在英 ...
- 【知识点整理】Oracle中NOLOGGING、APPEND、ARCHIVE和PARALLEL下,REDO、UNDO和执行速度的比较
[知识点整理]Oracle中NOLOGGING.APPEND.ARCHIVE和PARALLEL下,REDO.UNDO和执行速度的比较 1 BLOG文档结构图 2 前言部分 2.1 导读和注意事项 ...
- day 04 预科
目录 变量 什么是变量 变量的组成 变量名的命名规范 注释 单行注释 多行注释 turtle库的使用 今日内容 数据类型基础 变量 具体的值 存不是目的,取才是目的 为了描述世界万物的状态,因此有了数 ...