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下计算当前日期(当天)后N天出现NAN或者undefined的情况

    前言: 帮客户做一个订单系统,需要一个日期1,一个日期2,默认情况下日期1为当天,日期2为明天,只是当时有些疑惑的是日期2偶尔会出现NAN的情况,今天在segmentfault.com看到了同样的问题 ...

  2. SpringBoot与Cloud版本匹配问题

    在使用SpringCloud的时候需要在SpringBoot工程的pom.xml中引入 <dependencyManagement> <dependencies> <de ...

  3. Maven的pom.xml文件详解【转载】

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/20 ...

  4. HDU6095

    Rikka with Competition Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/O ...

  5. APP如何进行通信的

    什么是B/S架构(Browser/server):浏览器和服务器架构

  6. jquery之商城菜单

    实现效果:悬浮总菜单,显示分类菜单,移走隐藏总菜单,悬浮分类菜单,显示商品种类,移走隐藏商品种类和分类菜单,悬浮商品种类,显示商品种类和分类菜单,移走隐藏商品菜单和分类菜单. 代码如下: <!D ...

  7. 【读书笔记】iOS-访问网络

    iOS平台是按照一直有网络连接的思路来设计的,开发者利用这一特点创造了很多优秀的第三方应用.大多数的iOS应用都需要联网,甚至有些应用严重依赖网络,没有网络就无法正常工作. "在访问网络失败 ...

  8. SuperMap空间数据处理与制图操作短视频汇总

    转自:http://blog.csdn.net/supermapsupport/article/details/70227669 空间数据处理与制图是GIS系统建设最基础的部分,这里利用超图桌面软件- ...

  9. python自动化开发-9 进程 线程

    进程与线程 程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程.线程是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.一 ...

  10. Python 会是我们的未来吗?

    Python 热度激增 根据 Stack Overflow 的一项调查显示,Python 不仅在专业领域的使用率得到增长,在普通开发上的使用率也有所提升,有 40% 的受访者表示他们现在正在使用 Py ...