倍增求\(LCA\)

倍增基础

从字面意思理解,倍增就是“成倍增长”。

一般地,此处的增长并非线性地翻倍,而是在预处理时处理长度为\(2^n(n\in \mathbb{N}^+)\)的区间值。在这些预处理结果的基础上,我们可以进一步求出任意长度区间的答案。

比如区间最值问题\((RMQ)\)就可以使用倍增解决。对于每个起始点,预处理长度为\(2^n\)的区间最值。之后每段区间都可以以此求出,如:

\(f(1,7)=\max(f(1,4),f(3,7))\)

以上是最简单的一个举例。在计算机中,二进制的思想运用广泛。它能将复杂度中\(n\)因子化为\(log_2n\),还可以使用位运算进行常数优化。

\(LCA\)

LCA(Lowest Common Ancestors),即最近公共祖先,是指在有根树中,找出某两个结点u和v最近的公共祖先。——百度百科

定义很好理解。朴素的做法是预处理深度,将深度大的向上跳,再一起向上跳直到相遇。复杂度不好算,但绝对T飞。

注意到当两点相遇后,再如何跳都在同一个点上。所以就相当于\(LCA\)下不同,\(LCA\)上相同。这具有单调性,考虑二分。此处要对每一个点存储\(2^n\)级祖先,方法是预处理时递推。

我们记点i的\(2^k\)级祖先为\(fa[i][k]\)。

求解\(LCA\)的过程分为两步:一是提升到同一高度,二是同时向上跳。从最大的\(fa[x][log_2(depth[x])]\)开始逐次减半,相同则不跳,不同则跳。最终会到达\(LCA\)的下方。此时返回其中一个点的父亲,算法结束。

另外,预处理\(log_2\)的结果可以优化常数。

代码

#include<iostream>
#include<cmath>
#include<cstdio>
using namespace std;
int n,m,s,x,y;
const int MAXN=500005;
struct edge
{
int to,next;
}tree[MAXN*2];
int depth[MAXN],lg2[MAXN],head[MAXN],fa[MAXN][25],eg,temp;
int add(int from,int to)
{
tree[++eg].to=to;
tree[eg].next=head[from];
head[from]=eg;
} int get_depth(int node,int father)//当前点和父亲
{
depth[node]=depth[father]+1;
fa[node][0]=father; for(int i=1;i<=lg2[depth[node]]-1;i++)
fa[node][i]=fa[fa[node][i-1]][i-1];//node的2^i级父亲等于它的 2^(i-1)级父亲的2^(i-1)级父亲
for(int i=head[node];i;i=tree[i].next)
if(tree[i].to!=father) get_depth(tree[i].to,node);
} inline int LCA(int x,int y)
{
if(depth[x]<depth[y])
temp=x,x=y,y=temp;//确保x更深,便于处理
while(depth[x]>depth[y])
x=fa[x][lg2[depth[x]-depth[y]]-1];//跳到相同高度
if(x==y) return x;
for(int i=lg2[depth[x]]-1;i>=0;i--)
if(fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return fa[x][0];//注意返回的不是x,是x的父亲
} int main()
{
scanf("%d %d %d",&n,&m,&s);
for(int i=1;i<=n-1;i++)
scanf("%d %d",&x,&y),add(x,y),add(y,x);//建树 for(int i=1;i<=MAXN;i++)
lg2[i]=lg2[i-1]+(1<<lg2[i-1]==i);//预处理log
get_depth(s,0);//预处理2^n级父亲,深度等信息 for(int i=1;i<=m;i++)
scanf("%d %d",&x,&y),printf("%d\n",LCA(x,y));
return 0;
}

倍增求LCA学习笔记(洛谷 P3379 【模板】最近公共祖先(LCA))的更多相关文章

  1. 快速沃尔什变换(FWT)学习笔记 + 洛谷P4717 [模板]

    FWT求解的是一类问题:\( a[i] = \sum\limits_{j\bigoplus k=i}^{} b[j]*c[k] \) 其中,\( \bigoplus \) 可以是 or,and,xor ...

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

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

  3. 斜率优化dp学习笔记 洛谷P3915[HNOI2008]玩具装箱toy

    本文为原创??? 作者写这篇文章的时候刚刚初一毕业…… 如有错误请各位大佬指正 从例题入手 洛谷P3915[HNOI2008]玩具装箱toy Step0:读题 Q:暴力? 如果您学习过dp 不难推出d ...

  4. 边带权并查集 学习笔记 & 洛谷P1196 [NOI2002] 银河英雄传说 题解

    花了2h总算把边带权并查集整明白了qaq 1.边带权并查集的用途 众所周知,并查集擅长维护与可传递关系有关的信息.然而我们有时会发现并查集所维护的信息不够用,这时"边带权并查集"就 ...

  5. STL Stack(栈)学习笔记 + 洛谷 P1449 后缀表达式

    稍微看了看刘汝佳的白皮书,“实用主义”的STL实在是香到我了,而且在实验室大佬的推荐下我开始了stl的学习. 每篇附带一个题目方便理解,那行,直接开始. 毕竟是实用主义,所以就按照给的题目的例子来理解 ...

  6. STL Queue(队列)学习笔记 + 洛谷 P1540 机器翻译

    队(Queue) 队简单来说就是一个先进先出的“栈”,但是不同于标准“栈”的先进后出. 基本操作: push(x) 将x压入队列的末端 pop() 弹出队列的第一个元素(队顶元素),注意此函数并不返回 ...

  7. leetcood学习笔记-235-二叉搜索树的最近公共祖先

    题目描述: 利用二叉搜索树的特点,如果p.q的值都小于root,说明p q 肯定在root的左子树中:如果p q都大于root,说明肯定在root的右子树中,如果一个在左一个在右 则说明此时的root ...

  8. 洛谷P3373 [模板]线段树 2(区间增减.乘 区间求和)

    To 洛谷.3373 [模板]线段树2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格 ...

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

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

  10. Django 学习笔记(五)模板标签

    关于Django模板标签官方网址https://docs.djangoproject.com/en/1.11/ref/templates/builtins/ 1.IF标签 Hello World/vi ...

随机推荐

  1. 领域驱动设计(DDD)的实践经验分享之持久化透明

    原文:领域驱动设计(DDD)的实践经验分享之持久化透明 前一篇文章中,我谈到了领域驱动设计中,关于ORM工具该如何使用的问题.谈了很多我心里的想法,大家也对我的观点做了一些回复,或多或少让我深深感觉到 ...

  2. 演练:创建和使用静态库 (C++)

    我们将创建的下一个库类型是静态库 (LIB). 使用静态库是重用代码的一种绝佳方式. 您不必在自己创建的每个程序中重新实现同一例程,而只需对这些例程编写一次,然后从需要该功能的应用程序引用它们即可. ...

  3. Delphi 7下IGDIPlus库的使用

    IGDI+是一个免费开源封装微软GDI+功能的Delphi库,该库使得可以用Delphi语言代码快速简短的实现复杂GDI+应用程序.官方网站:http://www.mitov.com/html/igd ...

  4. YxdIocp包含有支持大并发的TCP服务组件、HTTP服务组件、UDP服务组件、WebSocket服务组件

    Delphi Windows IOCP 通讯模型封装,基于DIOCP.YxdIocp包含有支持大并发的TCP服务组件.HTTP服务组件.UDP服务组件.WebSocket服务组件,和TCP.UDP等基 ...

  5. ZooKeeper学习第五期--ZooKeeper管理分布式环境中的数据(转)

    转载来源:https://www.cnblogs.com/sunddenly/p/4092654.html 引言 本节本来是要介绍ZooKeeper的实现原理,但是ZooKeeper的原理比较复杂,它 ...

  6. 反向代理在APP通过无线路由接入3G/4G时的特殊作用

    由于某些需求,要临时搭建局域网络,给小范围内大量移动客户端提供特定的网络服务,这样的服务通常包含常见的HTTP服务.数据文件下载服务.RESTFul接口服务等.市面常见的非企业级WIFI路由器能够承受 ...

  7. Java中常用的url签名防篡改方法

    实现方式:Md5(url+key) 的方式进行的. 1.key可以是任意的字符串,然后“客户端”和“服务器端”各自保留一份,千万不能外泄. 2.请求的URL 例如:  name=lxl&age ...

  8. springboot中加分布式redis锁

    分布式redis锁,spring-boot-starter-data-redis,RedisTemplate 公司聊天的聊天系统,近期出现多个客服并发接待同一个客户的记录,经排查,是由于代码加的同步锁 ...

  9. UI-grid 表格内容可编辑(enableCellEdit可指定列编辑)

    在网上搜索了很多关于UI-Grid的问题 很遗憾好少啊啊啊 不过有API还是比较欣慰的 官方API:UI Grid 还有一位大佬的翻译的中文API:angularjs ui-grid中文api 行编辑 ...

  10. select下拉箭头样式重置

    select{ appearance:none; -moz-appearance:none; -webkit-appearance:none; background: url("../ima ...