#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
struct edge{
int next,to;
edge(){
}
edge(int a,int b){
next=a; to=b;
}
}E[];//建一个图……
int f[][],dep[],first[],tot;
int n,m,root;
void add_to_edge(int x,int y)
{
E[++t]=edge(first[x],y);
first[x]=t;
}//连边(无向图连两次哦)
void dfs(int x,int fa)
{
f[x][]=fa;
dep[x]=dep[fa]+;
//初始化 , 它的深度是它父亲的深度+1,x 往上倍增 2^0 层 是它的父亲
int k=ceil(log(dep[x])/log());//倍增上限
for (int i=; i<=k; i++)
{
f[x][i]=f[f[x][i-]][i-];
}//f数组存预处理的值,f[x][i]存的是x向上倍增
for (int i=first[x]; i; i=E[i].next)
{
int pos=E[i].to;
if (pos!=fa)//防止死循环,由于是存了两次所以E[i].to会连向它的父亲
dfs(pos,x);
}
}//预处理
int n,m,root;
void LCA()
{
if (dep[x]<dep[y]) swap(x,y);
int k1=dep[x]-dep[y];
int k2=ceil((log(n))/log());
for (int i=; i<=k2; i++)
{
if (k1&(<<i))
x=fa[x][i];
}//向上跳!!
if (x==y) return x;//两者在同一层并且相等那么x 就是它们的共同祖先
int k3=(log(dep[x])/log());
for (int i=k3; i>=; i--)
{
if (f[x][i]!=f[y][i])
{
x=f[x][i]; y=f[y][i];
}//倍增
}
return f[x][];
}
int main()
{
cin>>n>>m>>root;
for (int i=; i<=m; i++)
{
int x,y;
cin>>x>>y;
add_to_edge(x,y);
add_to_edge(y,s);
}
dfs(root,); }

还是济南集训的内容,让人头秃(不得不说两个老师讲了两遍我勉勉强强才搞懂一点点)


首先来看:

LCA的含义

Least  Common  Ancestors

LCA就是最近公共祖先,至于它的含义,我觉得例题写的看起来会更清楚,请看:

好的,明白了它的含义后,我们很容易想到朴素算法:

询问(x,y)的最近公共祖先,可以先向上枚举x的所有祖先,保存在数组Anc[]中。然后以相同的方法向上枚举y的所有祖先,当第一次发现有y的某个祖先k出现在Anc[]中,则输出k,算法结束。
 此时,每次查询复杂度为O( N )
(TLE警告哦)
那么其他方法呢,请看,这里介绍两种,树上倍增,与树链剖分(会在下一篇博客里写到)

树上倍增算法

核心思想:

  • 令F[x][n]表示x的2^n级祖先是谁.
  • 所以:F[x][n] = F[F[x][n – 1]][n – 1].
  • 对于两个点x, y.,求他们的LCA
  • 先把x, y提到同一高度.(方便向上进行倍增)
  • N从大到小枚举.(从高往低跳)
  • 查询F[x][n], F[y][n]是不是相等(比较倍增后的祖先,防止误判
  • 如果是的话说明n太大了,把n改小点.(最近公共祖先的祖先一定是他们的共同祖先
  • 不是的话就说明n不大,可以把x, y上移.(这个很容易理解吧)
 原理,如下图(图片来自老师的ppt)(它其实应该有个动图然而我不知道动图怎么传):

 看明白思路了吗,思路还是可以懂得吧,那我们来看代码,理解代码基本就能写了!
(不过我之前也始终看不明白代码就是了,思路都懂代码不会打,我可真是个小垃圾哦)
#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
const int maxn = ;
const int maxe = ;
int n,m,root; struct line
{
int from,to;
line(){}//空构造函数 line p;
line(int A,int B){
//构造函数 line L=line(1,2);
from=A;to=B;
}
}edge[maxe];
//上面是新建一个树 int last[maxn],_next[maxe],e;
//last[x]表示以x为起点的最后一条边(的编号)
//_next[i]表示与第i条边起点相同的上一条边(的编号) void add_edge(int x,int y)
{
edge[++e]=line(x,y);
_next[e]=last[x];
last[x]=e;
}
//存边 int Fa[maxn][],Dep[maxn]; void dfs(int x,int fa)
{
int i,k,y;
Fa[x][]=fa;//当前节点x的父亲节点fa
Dep[x]=Dep[Fa[x][]]+; //x的深度是它父亲节点的深度+1
//记录当前节点的深度
k=ceil(log(Dep[x])/log()); //ceil函数是向上取整
//x往上倍增的上限
for(i=;i<=k;i++)Fa[x][i]=Fa[Fa[x][i-]][i-];
//倍增计算祖先 ,记录
for(int i=last[x];i;i=_next[i])//枚举与x相邻的边
{
int v=edge[i].to;
if(v!=fa)dfs(v,x);
}
} int LCA(int x,int y)
{
int i,k,s;
s=ceil(log(n)/log()); //该树倍增最大可能的上限
if(Dep[x]<Dep[y])swap(x,y); //交换x和y的值
/////////////x往上走k层,让x与y处于同一层 //////////
k=Dep[x]-Dep[y];
for(i=;i<=s;i++)
if(k&(<<i))x=Fa[x][i];
if(x==y)return x; //x==y时,x就是最近公共祖先
///////////////////////////////////////////////////
s=ceil(log(Dep[x])/log()); //计算向上倍增的上限
for(i=s;i>=;i--)
if(Fa[x][i]!=Fa[y][i]){ x=Fa[x][i]; y=Fa[y][i]; }
return Fa[x][];
} int main()
{
int i,j,k;
cin>>n>>m>>root;
for(i=;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add_edge(x,y);
add_edge(y,x);//它是树,也就是无向图,所以存两次边
}
dfs(root,);//预处理
for(i=;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
printf("%d\n",LCA(x,y));
} }

代码(带注释)

OK吗?

这里建议去练练板子,指路-> https://www.luogu.org/problem/P3379

(哇我居然可以写蓝题了哎!!可喜可贺)

先到这里,如有问题欢迎指正

感谢观看   ありがとうございます

【一个蒟蒻的挣扎】LCA (倍增)的更多相关文章

  1. 【一个蒟蒻的挣扎】最小生成树—Kruskal算法

    济南集训第五天的东西,这篇可能有点讲不明白提前抱歉(我把笔记忘到别的地方了 最小生成树 概念:一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的 ...

  2. 【一个蒟蒻的挣扎】单源最短路(Dijkstra)

    赛前没啥时间好好解释了,还有三天2019CSP,大家加油啊!!! ヾ(◍°∇°◍)ノ゙ 背掉它就好啦!!! 我觉得我这一版打得还行就放上来了 #include<cstdio> #inclu ...

  3. noip2013Day2T3-华容道【一个蒟蒻的详细题解】

    描述 小 B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次.于是,他想到用编程来完成华容道:给定一种局面,华容道是否根本就无法完成,如果能完成,最少需要多少时间. 小 B 玩的华容道与经典的 ...

  4. 一个蒟蒻对FFT的理解(蒟蒻也能看懂的FFT)

    建议同学们先自学一下"复数(虚数)"的性质.运算等知识,不然看这篇文章有很大概率看不懂. 前言 作为一个典型的蒟蒻,别人的博客都看不懂,只好自己写一篇了. 膜拜机房大佬 HY 一. ...

  5. 这是一个蒟蒻的计划……QAQ

    感觉像我这种拖拉的人很有可能是完成不了的,挂上来相当于监督我自己啦QWQ [学习计划] [√]1.去看Trie树!!!   yyb学长的blog 2.KMP还有AC自动机 先贴两个链接在这里吧:KMP ...

  6. 一个蒟蒻的解题过程记录——洛谷P1003 铺地毯

    这到题算是我“火线回归”后码的第一道题,病好了心情不错,发篇博客分享一下 目录: ·题目描述 ·题目分析 ·解题思路 ·代码实现 ·总结 ·题目描述: 为了准备一场特殊的颁奖典礼,组织者在会场的一片矩 ...

  7. 论一个蒟蒻的脑子里可以有多少坑(貌似咕了……目前更新保持在noip阶段)

    就是错题整理了,其实也会把一些不该犯的失误整进来. 其实之前一直拖着不想写,直到某次模拟赛,看错了2道题,顺便爆了一道题的int(没错第一个点就会爆)之后爆零了,吓得我赶紧把这篇博客搞出来了..... ...

  8. 蒟蒻kc的垃圾数列

    题目背景 在某教练的强迫之下,我一个蒟蒻居然出题了!!!出题了!!!(数据太水别找我qwq) 好的,JL说好的一题100快拿来 题目描述 首先,给你一个空的长度为n的序列(废话) 然后,你有一系列神奇 ...

  9. 算法描述》LCA两三事(蒟蒻向)

    LCA是图论中常用的解决树形结构子问题的工具,这一问题一般需要用一个简短的子函数直接解决,但是这对于广大蒟蒻们仍然是一个不小的问题. LCA是指在树形结构中两点的最近公共祖先,对于这个问题,直接向上找 ...

随机推荐

  1. DOM导航与DOM事件

    HTML DOM 导航 通过 HTML DOM,能够使用节点关系在节点树中导航. ㈠HTML DOM 节点列表 getElementsByTagName() 方法返回节点列表.节点列表是一个节点数组. ...

  2. codevs 5969 [AK]刻录光盘x

                         题目描述 Description • 在FJOI2010夏令营快要结束的时候,很多营员提出来要把整个夏令营期间的资料刻录成一张光盘给大家,以便大家回去后继续学 ...

  3. Qtcreator 之中文目录

    由于编码格式问题,  qtcreator 所有关于目录的引用和存放均不出现中文,否则编译可以通过,运行时 crashed !!!

  4. python学习之路(13)

    列表生成式 列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式. 举个例子,要生成list [1, 2, 3, 4, 5, 6, 7, ...

  5. LeetCode 93. 复原IP地址(Restore IP Addresses)

    题目描述 给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式. 示例: 输入: "25525511135" 输出: ["255.255.11.135&qu ...

  6. leetcode-easy-sorting and searching- 278 First Bad Version

    mycode  96.42 # The isBadVersion API is already defined for you. # @param version, an integer # @ret ...

  7. AM中修改套料板的尺寸

  8. Android 面试汇总<三>

    1.3 计算机网络 基础 Q:五层协议的体系结构分别是什么?每一层都有哪些协议? 技术点:网络模型.协议 思路:分条解释每层名字以及协议 参考回答: 物理层 数据链路层:逻辑链路控制LLC.媒体接入控 ...

  9. zabbix服务器性能监控工具的安装一

    Linux下安装zabbix需要有LNMP运行环境,本篇记录安装LNMP的过程 目录 1.准备工作 2.安装mysql 3.安装nginx 4.安装php 1.准备工作 1.1准备一台centos6. ...

  10. WebSocket-Node

    WebSocket Client & Server Implementation for Node 参考资料:[https://github.com/theturtle32/WebSocket ...