LCA(Lowest Common Ancestor 最近公共祖先)定义如下:在一棵树中两个节点的LCA为这两个节点所有的公共祖先中深度最大的节点。

比如这棵树

结点5和6的LCA是2,12和7的LCA是1,8和14的LCA是4。

这里讲一下用树链剖分来求LCA。

先想一下,若要求结点13和4的LCA,那很显然是4,因为他们在一条重链上。所谓的重链,就是取每个结点u的所有子节点中,子树最大的子结点v,然后将边(u,v)作为重边,其余边作为轻边,重边构成的链就是重链。子树最大就是指该点所得孩子结点最多(这里要包括他自己)。

我们先找出所有的重链。

可见这棵树有7条重链(包括一条链只有一个结点的)。每一条重链的顶点就是该链上深度最小的结点。

而树链剖分的目标就是将要求的两个点转换到一条重链上,这样LCA就是该条重链上深度较小的结点了。

具体实现步骤拿第一幅图中的结点12和14举例。首先要比较的是12和14所在链的顶点的深度,可见12所在链的顶点更深,此时将12跳到它的顶点12的父亲结点6。然后再比较6所在链的顶点和14所在链的顶点,循环下去直到两个点到同一个链上,最后比较,收尾。

这就是树链剖分的基本思想了,那我就开始写了。

首先跑两遍dfs,第一遍是建树和建链,第二遍是记录每一个结点的顶点(这样就知道该点所在链的顶点的深度了)。然后就是用上述思想求LCA。

我们以洛谷的板子为例,传送门:https://www.luogu.org/problemnew/show/P3379

上代码(懒得用邻接表存图了,上vector)

其中vis数组是为了解决无向图存两次边的问题。

 #include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
const int maxn = 5e5 + ;
vector<int>v[maxn];
/*第一遍dfs主要来维护以下这些数组,size[now]指结点now的子树大小,dep[now]指结点now的深度,
Maxson[now]指now所在链上结点now的下一个结点(用来建链) */
int vis[maxn], size[maxn], dep[maxn], fa[maxn], Maxson[maxn];
void dfs1(int now)
{
vis[now] = ; size[now]= ;
for(int i = ; i < v[now].size(); ++i)
if(!vis[v[now][i]])
{
dep[v[now][i]] = dep[now] + ;
fa[v[now][i]] = now;
dfs1(v[now][i]);
size[now] += size[v[now][i]]; //结点now的子树大小就是他所有孩子结点的大小之和加1(包括自己)
if(size[v[now][i]] > size[Maxson[now]]) Maxson[now] = v[now][i]; //选重边
}
}
int path[maxn];
void dfs2(int now)
{
vis[now] = ;
for(int i = ; i < v[now].size(); ++i)
if(!vis[v[now][i]])
{
if(Maxson[now] == v[now][i]) path[v[now][i]] = path[now];
else path[v[now][i]] = v[now][i]; //新开辟一条链
dfs2(v[now][i]);
}
}
int lca(int x, int y)
{
while(path[x] != path[y]) //若不在一条链上
{
if(dep[path[x]] > dep[path[y]]) x = fa[path[x]];
else y = fa[path[y]];
}
return dep[x] < dep[y] ? x : y;
}
int main()
{
int n, m, s; scanf("%d%d%d", &n, &m, &s);
for(int i = ; i < n; ++i)
{
int a, b; scanf("%d%d", &a, &b);
v[a].push_back(b); v[b].push_back(a);
}
dep[s] = ; memset(vis, , sizeof(vis));
dfs1(s);
path[s] = s; memset(vis, , sizeof(vis));
dfs2(s);
for(int i = ; i <= m; ++i)
{
int a, b; scanf("%d%d", &a, &b);
printf("%d\n", lca(a, b));
}
return ;
}

我们再来分析一下时间复杂度:任意一个结点到根的路径,每遇到一条轻边,子树大小就至少翻一倍,所以最坏情况下是O(logn),很牛吧?

洛谷的这个毒瘤板子题,我以前用RMQ和树上倍增写都会T两个点,加快读快输开氧气勉强过了,但是很不爽。直到有一天我会了树剖后,竟然直接AC,贼激动。

那我就讲到这了。啊对了,他有一个缺点,难写(还是RMQ简单)

LCA树链剖分的更多相关文章

  1. Count on a tree SPOJ 10628 主席树+LCA(树链剖分实现)(两种存图方式)

    Count on a tree SPOJ 10628 主席树+LCA(树链剖分实现)(两种存图方式) 题外话,这是我第40篇随笔,纪念一下.<( ̄︶ ̄)↗[GO!] 题意 是说有棵树,每个节点上 ...

  2. [BZOJ3626] [LNOI2014]LCA(树链剖分)

    [BZOJ3626] [LNOI2014]LCA(树链剖分) 题面 给出一棵N个点的树,要求支持Q次询问,每次询问一个点z与编号为区间[l,r]内的点分别求最近公共祖先得到的最近公共祖先深度和.N, ...

  3. BZOJ 3626: [LNOI2014]LCA [树链剖分 离线|主席树]

    3626: [LNOI2014]LCA Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2050  Solved: 817[Submit][Status ...

  4. Codeforces Round #329 (Div. 2) D. Happy Tree Party LCA/树链剖分

    D. Happy Tree Party     Bogdan has a birthday today and mom gave him a tree consisting of n vertecie ...

  5. BZOJ 3626: [LNOI2014]LCA( 树链剖分 + 离线 )

    说多了都是泪啊...调了这么久.. 离线可以搞 , 树链剖分就OK了... -------------------------------------------------------------- ...

  6. [CodeVS2370] 小机房的树 (LCA, 树链剖分, LCT)

    Description 小机房有棵焕狗种的树,树上有N个节点,节点标号为0到N-1,有两只虫子名叫飘狗和大吉狗,分居在两个不同的节点上.有一天,他们想爬到一个节点上去搞基,但是作为两只虫子,他们不想花 ...

  7. BZOJ3626[LNOI2014]LCA——树链剖分+线段树

    题目描述 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q次询问,每次询 ...

  8. bzoj 3626 : [LNOI2014]LCA (树链剖分+线段树)

    Description 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q ...

  9. 【bzoj3626】[LNOI2014]LCA 树链剖分+线段树

    题目描述 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q次询问,每次询 ...

随机推荐

  1. js获取带#号链接后的参数

    现在许多的主流网站都将'#'大规模用于重要URL中,我们通过正则表达式和window.location.search获取参数已经行不通了. 一.'#'号是什么 1.#代表网页中的一个位置.其后面的字符 ...

  2. c# 过滤html

    public static string StripHTML(string html) { try { System.Text.RegularExpressions.Regex regex1 = ne ...

  3. C# if---else---练习题整理

    if    else    语句是到今天为止学习的第一个完整的语句,把有意思的练习题整理下来开一下脑洞!!! 练习一简单的人工智能 1 static void Main(string[] args) ...

  4. HDFS的副本存放策略(全)

    HDFS的副本存放策略   HDFS作为Hadoop中的一个分布式文件系统,而且是专门为它的MapReduce设计,所以HDFS除了必须满足自己作为分布式文件系统的高可靠性外,还必须为MapReduc ...

  5. ios --xib自定义,解决在导航栏不透明的情况下,自定义xib view高度被压缩64的问题

    在使用xib自定义view的时候,个人习惯性的直接使用xib中的约束,所以自然而然的要打开Autolayout.以前在使用的时候没有发现什么问题,最近项目中使用的时候突然发现在导航栏透明的情况下,出现 ...

  6. 5.数码相框-额外项目电子书总结,并使用svgalib库

    在LCD显示任意编码的文本文件,类似电子书 怎样在LCD上显示文件: 需要哪几个文件? 1.顶部文件 通过main.c分析命令行的操作,然后初始化各个管理文件下的结构体,比如DisplayInit() ...

  7. 10种JavaScript开发者必备的VS Code插件

    摘要: 好的代码插件可以让工作效率翻倍,心情也更加舒畅! 原文:10 Must-have VS Code Extensions for JavaScript Developers 作者:Michael ...

  8. 设置div背景透明的CSS样式

    div背景透明样式: 样式代码: .alert{filter:alpha(opacity=100); /* IE */ -moz-opacity:1.0; /* Moz + FF */ opacity ...

  9. JS的防抖,节流,柯里化和反柯里化

    今天我们来搞一搞节流,防抖,柯里化和反柯里化吧,是不是一看这词就觉得哎哟wc,有点高大上啊.事实上,我们可以在不经意间用过他们但是你却不知道他们叫什么,没关系,相信看了今天的文章你会有一些收获的 节流 ...

  10. uCrop图片裁剪

    uCrop使用 github地址 https://github.com/Yalantis/uCrop然后clone或下载到本地,运行之. 效果预览 app/build.gradle compile ' ...