树剖LCA讲解
LCA的类型多种多样,只说我知道的,就有倍增求LCA,tarjin求LCA和树链剖分求LCA,当然,也还有很多其他的方法。
其中最常用,速度最快的莫过于树链剖分的LCA了。
树链剖分,首先字面理解一下,什么是树链剖分。
就是把一棵树剖分为若干条链,然后利用数据结构(树状数组,SBT,Splay,线段树等等)去维护每一
条链,复杂度为O(logn)
那么,树链剖分的第一步当然是对整棵树进行遍历,预处理一些要用的变量。
void dfs(int now){
siz[now]=;
deep[now]=deep[dad[now]]+;
for(int i=head[now];i;i=net[i])
if(to[i]!=dad[now]){
dad[to[i]]=now;
dfs(to[i]);
siz[now]+=siz[to[i]];
}
}
其中dad[i]=j表示节点i的爸爸是j,deep[i]表示节点i的深度,siz[i]表示以节点i为祖先的子树的大小(也就是这个子树里节点的个数)
第二步就是对树进行轻重边的划分,这样我们就可以保证每一个点属于且只属于一条链。
在这棵树中其实,只有重链是我们用的到的,轻链一般就被忽略了。
而 我们一般是通过儿子个数的多少来划分出轻重边的,在所有的儿子结点中,各以他们为祖先的子树的节点的个数多的儿子节点与其父亲的连边就是一条重边。这句话有点绕口,看图理解一下:(因为这棵树不一定是二叉树,一个节点可能有多个儿子,这这例子是一个二叉树的,不太全面。)
在这个图里面6和9的连边就是一条重边,而6和7的连边就是一条轻边。类似的,9这个节点有两个儿子,以这两个儿子为祖先的子树的大小是相同的,所以随便找一条
边作为重边就可以了,而7只有一个子节点所以7和8的链理所因当就是重链了。
对于这样的一颗树来说,他的轻重链分布如下所示:其中,灰色粗线表示重边,黑色细线表示轻边。
这是他的边的分布情况:
这是找轻重链的代码:其中top[i]表示点i沿他的重边上去,最高能到达的点。
比如上面的图里面每个点的top分别是:1,2,2,2,1,1,7,7,1,10,1,12,12
void dfs1(int x){
int t=;
if(!top[x]) top[x]=x;
for(int i=head[x];i;i=net[i])
if(to[i]!=dad[x]&&siz[to[i]]>siz[t])
t=to[i];
if(t){
top[t]=top[x];
dfs1(t);
}
for(int i=head[x];i;i=net[i])
if(to[i]!=dad[x]&&t!=to[i])
dfs1(to[i]);
}
找完轻重链后,就可以进行下一步操作了,找最近公共祖先。
先看一下代码:
int lca(int x,int y){
for(;top[x]!=top[y];){
if(deep[top[x]]<deep[top[y]])
swap(x,y);
x=dad[top[x]];
}
if(deep[x]>deep[y])
swap(x,y);
return x;
}
代码是非常好理解的,就是一个while语句,当两个点位于一条重链上的时候结束操作。
否则的话,深度深的点,顺着他所在的重链向上跳。跳到这条重链的上一个点的位置。在两个点位于一条链上之后,深度浅的点的位置,就是我们要找的公共祖先。
还是这个图,如果要求8和11的最近公共祖先。第一步就是让8顺着边向上跳他会跳到为位置6的点上去,然后6和11位于同一条链上,6的深度浅,6就是我们要求的最近公共祖先。
再举一个复杂点的例子:如果要求10和13的最近公共祖先要怎么呢?
首先,找到两个点中深度较深的点,是10,10号点向上跳,跳到9号点,然后是13号点向上跳,回跳到5号点,这是5和9位于同一条链上,5号点就是我们所求的最近公共祖先。
有人可能会产生这样的问题:我在跳边的时候,为什么要跳到重链顶点的父亲,而不跳到重链顶点,这样万一错过了最近公共祖先肿么办?
那就要请你自己好好考虑了,因为这个问题在上面已经回答过了。
第一个问题:如果跳到顶点的话,像是上面的图中的10号点就跳不动了,那公共祖先就出不来了。
第二个问题:不会。因为除了叶子结点,每一个节点都一定属于一条重链。
答案
题目描述
如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。
输入输出格式
输入格式:
第一行包含三个正整数N、M、S,分别表示树的结点个数、询问的个数和树根结点的序号。
接下来N-1行每行包含两个正整数x、y,表示x结点和y结点之间有一条直接连接的边(数据保证可以构成树)。
接下来M行每行包含两个正整数a、b,表示询问a结点和b结点的最近公共祖先。
输出格式:
输出包含M行,每行包含一个正整数,依次为每一个询问的结果。
输入输出样例
说明
时空限制:1000ms,128M
数据规模:
对于30%的数据:N<=10,M<=10
对于70%的数据:N<=10000,M<=10000
对于100%的数据:N<=500000,M<=500000
样例说明:
该树结构如下:
第一次询问:2、4的最近公共祖先,故为4。
第二次询问:3、2的最近公共祖先,故为4。
第三次询问:3、5的最近公共祖先,故为1。
第四次询问:1、2的最近公共祖先,故为4。
第五次询问:4、5的最近公共祖先,故为4。
故输出依次为4、4、1、4、4。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define MAXN 500010
using namespace std;
int n,m,s,tot;
int to[MAXN*],net[MAXN*],head[MAXN];
int dad[MAXN],deep[MAXN],siz[MAXN],top[MAXN];
void add(int u,int v){
to[++tot]=v;net[tot]=head[u];head[u]=tot;
to[++tot]=u;net[tot]=head[v];head[v]=tot;
}
void dfs(int now){
siz[now]=;
deep[now]=deep[dad[now]]+;
for(int i=head[now];i;i=net[i])
if(to[i]!=dad[now]){
dad[to[i]]=now;
dfs(to[i]);
siz[now]+=siz[to[i]];
}
}
void dfs1(int x){
int t=;
if(!top[x]) top[x]=x;
for(int i=head[x];i;i=net[i])
if(to[i]!=dad[x]&&siz[to[i]]>siz[t])
t=to[i];
if(t){
top[t]=top[x];
dfs1(t);
}
for(int i=head[x];i;i=net[i])
if(to[i]!=dad[x]&&t!=to[i])
dfs1(to[i]);
}
int lca(int x,int y){
for(;top[x]!=top[y];){
if(deep[top[x]]<deep[top[y]])
swap(x,y);
x=dad[top[x]];
}
if(deep[x]>deep[y])
swap(x,y);
return x;
}
int main(){
scanf("%d%d%d",&n,&m,&s);
for(int i=;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
}
dfs(s);
dfs1(s);
for(int i=;i<=n;i++) cout<<top[i]<<" ";
for(int i=;i<=m;i++){
int u,v;
scanf("%d%d",&u,&v);
cout<<lca(u,v)<<endl;
}
}
/*
13 1 1
1 2
2 3
3 4
1 5
5 6
6 7
7 8
6 9
9 10
9 11
5 12
12 13
*/
练习题:
cogs 2478. [HZOI 2016]简单的最近公共祖先
洛谷 P3128 [USACO15DEC]最大流Max Flow
树剖LCA讲解的更多相关文章
- 树剖 lca
GeneralLiu 橙边为轻边 红边为重边 绿数为每个点的 top 橙数为每个点的编号 步骤 1 先预处理 每个点的 deep深度 size子树大小 dad父节点 2 再预处理 每个点的 to ...
- BZOJ_2286_[Sdoi2011]消耗战_虚树+树形DP+树剖lca
BZOJ_2286_[Sdoi2011]消耗战_虚树+树形DP Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的 ...
- bzoj 3307: 雨天的尾巴【树剖lca+树上差分+线段树合并】
这居然是我第一次写线段树合并--所以我居然在合并的时候加点结果WAWAWAMLEMLEMLE--!ro的时候居然直接指到la就行-- 树上差分,每个点建一棵动态开点线段树,然后统计答案的时候合并即可 ...
- 【BZOJ 3626】 [LNOI2014]LCA【在线+主席树+树剖】
题目链接: TP 题解: 可能是我比较纱布,看不懂题解,只好自己想了…… 先附一个离线版本题解[Ivan] 我们考虑对于询问区间是可以差分的,然而这并没有什么卵用,然后考虑怎么统计答案. 首先LC ...
- tarjan,树剖,倍增求lca
1.tarjan求lca 思想: void tarjan(int u,int f){ for(int i=---){//枚举边 if(v==f) continue; dfs(v); //继续搜 uni ...
- BZOJ 3626 [LNOI2014]LCA:树剖 + 差分 + 离线【将深度转化成点权之和】
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3626 题意: 给出一个n个节点的有根树(编号为0到n-1,根节点为0,n <= 50 ...
- POJ3417Network(LCA+树上查分||树剖+线段树)
Yixght is a manager of the company called SzqNetwork(SN). Now she's very worried because she has jus ...
- 【树剖求LCA】树剖知识点
不太优美但是有注释的版本: #include<cstdio> #include<iostream> using namespace std; struct edge{ int ...
- 树链剖分 树剖求lca 学习笔记
树链剖分 顾名思义,就是把一课时分成若干条链,使得它可以用数据结构(例如线段树)来维护 一些定义: 重儿子:子树最大的儿子 轻儿子:除了重儿子以外的儿子 重边:父节点与重儿子组成的边 轻边:除重边以外 ...
随机推荐
- css选择器---继承,优先级,层叠
CSS三大特性—— 继承. 优先级和层叠. 继承:即子类元素继承父类的样式; 优先级:是指不同类别样式的权重比较; 层叠:是说当数量相同时,通过层叠(后者覆盖前者)的样式. css选择符分类 首先来看 ...
- Flask小总结+实例化Flask参数以及对app的配置
Flask 小而精 三方组件全 稳定性相对较差 1.启动: from flask import Flask app = Flask(__name__) app.run("0.0.0.0&qu ...
- BZOJ 2001 线段树+LCT (TLE)
同是O(nlog^2n)怎么常数差距就这么大呢,,, 同是LCT 怎么我的和Po姐姐的常数差距就这么大呢 我绝对是脑子被驴踢了才写这个垃圾算法 //By SiriusRen #include < ...
- 去掉myeclipse的预览窗口
1,选择菜单: windows -> preferences2,在弹出窗口中选择General-> Editors -> FileAssociations3,在上方框内选择*.jsp ...
- const修饰规则 及其 用法
const指针和指向const变量的指针,在写法上容易让人混淆,记住一个规则:从左至右,依次结合,const就近结合. 比如,int * const p: 1.int * (const p):变量p经 ...
- vs2010 视图 aspx页面设计窗口创建控件时出错 未将对象引用设置到对象的实例
第一步,首先关闭aspx页面 第二步,在单击项目右击,选择“清理” 第三步,然后在打开aspx页面,就可以看到正常的页面了. 注:一次不行的会,多做几次. 如果还是不行的话,你看看你.cs页面是否继承 ...
- Jquery音频播放插件下载地址(有Html、JS、CSS、音频)
有详细的html文件.全部JS代码文件.Css样式文件.测试音频资料 音频播放插件下载链接(百度云): http://pan.baidu.com/s/1pKC904F 提取码评论留邮箱发送,谢谢!
- 如何卸载系统自带的Microsoft Office
(1)首先.在C盘删除office文件夹. (2)删除注册表 1)开始菜单-->运行-->regedit进入注册表 (window+r -->) 2)在注册表里找到HKEY_CUR ...
- JS——this与new
this: 1.this只出现在函数中 2.谁调用函数,this就指的是谁 3.new People的this指的就是被创建的对象实例 new: 1.开辟内存空间,存储新创建的对象 2.把this设置 ...
- Codeforces_732D_(二分贪心)
D. Exams time limit per test 1 second memory limit per test 256 megabytes input standard input outpu ...