定义

  • 最近公共祖先简称 \(LCA\)
  • 两个节点的最近公共祖先,就是这两个点的公共祖先里,离根最远的的那个
  • 为了方便,我们记某点集 \(S={v1,v2,...,vn}\) 的最近公共祖先为 \(LCA(v1,v2,...,vn)\) 或 \(LCA(S)\)

LCA的有用的性质

  • \(1.\) \(lca(x)=x\)
  • \(2.\) 每两个点的 \(lca\) 唯一
  • \(3.\) 如果 \(x\) 与 \(y\) 互相不是对方的祖先,则两点位于 \(lca(x,y)\) 的两颗不同子树中
  • \(4.\) \(重要\) 两点的 \(lca\) 定在两点的最短路上 (那么 \(x\) 到 \(y\) 的最短路即为 \(x->...->lca(x,y)->...->y\))
  • \(5.\) 由第4条可推出,\(d(x,y)=h(x)+h(y)-2h(lca(x,y))\) (d是两点距离,h为点到根的距离)

实现算法

  • 本人只会树剖算法 \(qwq\)
  • 先来两遍 \(dfs\) ,然后 \(lca\) ,属于在线算法,时间复杂度为 \(O(2*n+n*log(n))\)
  • 下面代码求的是两点的 \(lca\)
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=5e5+1;
int n,m,s;
int fa[N],son[N],dep[N],top[N],sz[N];
vector<int>e[N];
template<typename Tp> inline void read(Tp&x)
{
x=0;register bool z=1;
register char c=getchar();
for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
x=(z?x:~x+1);
}
void dfs1(int x,int father)
{
fa[x]=father,dep[x]=dep[father]+1,sz[x]=1;
for(int y:e[x])
if(y!=father)
{
dfs1(y,x);
sz[x]+=sz[y];
if(sz[son[x]]<sz[y]) son[x]=y;
}
}
void dfs2(int x,int t)
{
top[x]=t;
if(!son[x]) return ;
dfs2(son[x],t);
for(int y:e[x])
if(y!=fa[x]&&y!=son[x])
dfs2(y,y);
}
int lca(int x,int y)
{
for(;top[x]!=top[y];x=fa[top[x]])
if(dep[top[x]]<dep[top[y]]) swap(x,y);
return dep[x]<dep[y]?x:y;
}
signed main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
int x,y;
read(n),read(m),read(s);
for(int i=1;i<n;i++)
read(x),read(y),
e[x].push_back(y),
e[y].push_back(x);
dfs1(s,0),dfs2(s,s);
while(m--)
read(x),read(y),
cout<<lca(x,y)<<endl;
}
  • 样例输入

    12 5 1

    1 2

    1 3

    1 4

    2 5

    2 6

    3 7

    4 8

    4 9

    6 10

    8 11

    8 12

    5 6

    10 5

    5 9

    7 9

    11 12
  • 样例输出

    2

    2

    1

    1

    8

如何A题

  • \(1.\) 常与 \(线段树\) \(树状数组\) \(最短路\) 等算法同用。
  • \(2.\) \(lca\) 只能在树中使用,否则会 \(RE\) 所以有环图中需要 \(kru\) 重构树
  • \(3.\) 有时用到边权但不好解决时,可以在 \(x\)、\(y\)两点中插入一个点,使其点权为原边权,最后输出点权即可
  • 在 \(货车运输\) 中运用了前两条,详见 货车运输 题解链接
  • \(4.\) 最短路时,两点间最短距离用到前面的 \(性质5\),例题 Distance Queries 距离咨询
  • \(5.\) 由于我们没有学习倍增的 \(lca\) ,所以有时超时时要用到树状数组的 \(差分\) ,例题DFS 序 3,树上差分 1

    ,通常修改两点最短路径上所有权值时要用到
  • \(Distance Queries 距离咨询\) 代码
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
#define f t[p]
#define ls p<<1
#define rs p<<1|1
using namespace std;
const int N=4e5+1;
template<typename Tp> inline void read(Tp&x)
{
x=0;register bool z=1;
register char c=getchar();
for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
x=(z?x:~x+1);
}
int n,m,k,cnt;
int a[N],fa[N],son[N],dep[N],top[N],sz[N],d[N];
int head[N],to[N],nxt[N],w[N],tot;
void add(int x,int y,int z)
{
nxt[++tot]=head[x];
to[tot]=y;
w[tot]=z;
head[x]=tot;
}
void dfs1(int x,int t)
{
fa[x]=t,dep[x]=dep[t]+1,sz[x]=1;
for(int i=head[x],y;i;i=nxt[i])
if((y=to[i])!=t)
{
dfs1(y,x);
sz[x]+=sz[y];
if(sz[son[x]]<sz[y]) son[x]=y;
}
}
void dfs2(int x,int t)
{
top[x]=t;
if(!son[x]) return ;
dfs2(son[x],t);
for(int i=head[x],y;i;i=nxt[i])
if((y=to[i])!=fa[x]&&y!=son[x])
dfs2(y,y);
}
void build(int x)
{
for(int i=head[x],y;i;i=nxt[i])
if((y=to[i])!=fa[x])
d[y]=d[x]+w[i],
build(y);
}
int lca(int x,int y)
{
for(;top[x]!=top[y];x=fa[top[x]])
if(dep[top[x]]<dep[top[y]]) swap(x,y);
return dep[x]<dep[y]?x:y;
}
signed main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
int x,y,z;char s;
read(n),read(m);
while(m--)
read(x),read(y),read(z),cin>>s,
add(x,y,z),add(y,x,z);
dfs1(1,0),dfs2(1,1),build(1);
read(k);
while(k--)
read(x),read(y),
cout<<(d[x]+d[y]-d[lca(x,y)]*2)<<endl;//x到根节点的距离+y到根节点的距离-2*公共祖先到根节点的距离
}
  • \(DFS 序 3,树上差分 1\) 代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int N=1e6+10;
template<typename Tp> inline void read(Tp&x)
{
x=0;register bool z=1;
register char c=getchar();
for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
x=(z?x:~x+1);
}
int n,m,root,cnt;
int a[N],fa[N],son[N],rk[N],sz[N],in[N],out[N],dep[N],top[N],c1[N],c2[N];
vector<int>e[N];
void dfs1(int x,int t)
{
fa[x]=t,dep[x]=dep[t]+1,sz[x]=1,in[x]=++cnt,rk[cnt]=x;
for(int y:e[x])
if(y!=t)
{
dfs1(y,x);
sz[x]+=sz[y];
if(sz[son[x]]<sz[y]) son[x]=y;
}
out[x]=cnt;
}
void dfs2(int x,int t)
{
top[x]=t;
if(!son[x]) return ;
dfs2(son[x],t);
for(int y:e[x])
if(y!=fa[x]&&y!=son[x])
dfs2(y,y);
}
int lca(int x,int y)
{
for(;top[x]!=top[y];x=fa[top[x]])
if(dep[top[x]]<dep[top[y]]) swap(x,y);
return dep[x]<dep[y]?x:y;
}
int lowbit(int x){return x&(~x+1);}
void change(int x,int y)
{
if(x)
for(int i=x;i<=n;i+=lowbit(i))
c1[i]+=y,
c2[i]+=(dep[rk[x]]*y);
}
void change(int x,int y,int z)
{
int f=lca(x,y);
change(in[x],z),change(in[y],z),
change(in[f],~z+1),change(in[fa[f]],~z+1);
}
int ask1(int x)
{
int ans=0;
while(x) ans+=c1[x],x-=lowbit(x);
return ans;
}
int ask2(int x)
{
int ans=0;
while(x) ans+=c2[x],x-=lowbit(x);
return ans;
}
int ask_it(int x) {return (ask1(out[x])-ask1(in[x]-1));}
int ask_tree(int x) {return ask2(out[x])-ask2(in[x]-1)-(dep[x]-1)*ask_it(x);}
signed main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
int x,y,z,s;
read(n),read(m),read(root);
for(int i=1;i<=n;i++) read(a[i]);
for(int i=1;i<n;i++)
read(x),read(y),
e[x].push_back(y),
e[y].push_back(x);
dfs1(root,0),dfs2(root,root);
for(int i=1;i<=n;i++) change(i,i,a[i]);
while(m--)
{
read(s);
if(s==1) read(x),read(y),read(z),change(x,y,z);
else if(s==2) read(x),cout<<ask_it(x)<<endl;
else read(x),cout<<ask_tree(x)<<endl;
}
}

lca 学习笔记的更多相关文章

  1. LCA学习笔记

    写在前面 目录 一.LCA的定义 二.暴力法求LCA 三.倍增法求LCA 四.树链剖分求LCA 五.LCA典型例题 题目完成度 一.LCA的定义 LCA指的是最近公共祖先.具体地,给定一棵有根树,若结 ...

  2. 倍增LCA学习笔记

    前言 ​ "倍增",作为一种二进制拆分思想,广泛用于各中算法,如\(ST\)表,求解\(LCA\)等等...今天,我们仅讨论用该思想来求解树上两个节点的\(LCA\)(最近公共祖先 ...

  3. 倍增求LCA学习笔记(洛谷 P3379 【模板】最近公共祖先(LCA))

    倍增求\(LCA\) 倍增基础 从字面意思理解,倍增就是"成倍增长". 一般地,此处的增长并非线性地翻倍,而是在预处理时处理长度为\(2^n(n\in \mathbb{N}^+)\ ...

  4. 树链剖分 树剖求lca 学习笔记

    树链剖分 顾名思义,就是把一课时分成若干条链,使得它可以用数据结构(例如线段树)来维护 一些定义: 重儿子:子树最大的儿子 轻儿子:除了重儿子以外的儿子 重边:父节点与重儿子组成的边 轻边:除重边以外 ...

  5. LCA学习笔记(原洛谷文章)

    本文原发布时间:\(\texttt{2022-05-21 14:11:52}\) 简介 最经公共祖先 \(\operatorname{LCA}(a,b)=c\),指的是在一棵树上节点 \(a\) 与 ...

  6. 算法学习笔记(5): 最近公共祖先(LCA)

    最近公共祖先(LCA) 目录 最近公共祖先(LCA) 定义 求法 方法一:树上倍增 朴素算法 复杂度分析 方法二:dfs序与ST表 初始化与查询 复杂度分析 方法三:树链剖分 DFS序 性质 重链 重 ...

  7. kruskal重构树学习笔记

    \(kruskal\) 重构树学习笔记 前言 \(8102IONCC\) 中考到了,本蒟蒻不会,所以学一下. 前置知识 \(kruskal​\) 求最小(大)生成树,树上求 \(lca​\). 算法详 ...

  8. 仙人掌&圆方树学习笔记

    仙人掌&圆方树学习笔记 1.仙人掌 圆方树用来干啥? --处理仙人掌的问题. 仙人掌是啥? (图片来自于\(BZOJ1023\)) --也就是任意一条边只会出现在一个环里面. 当然,如果你的图 ...

  9. Day 4 学习笔记 各种图论

    Day 4 学习笔记 各种图论 图是什么???? 不是我上传的图床上的那些垃圾解释... 一.图: 1.定义 由顶点和边组成的集合叫做图. 2.分类: 边如果是有向边,就是有向图:否则,就是无向图. ...

  10. 树上启发式合并(dsu on tree)学习笔记

    有丶难,学到自闭 参考的文章: zcysky:[学习笔记]dsu on tree Arpa:[Tutorial] Sack (dsu on tree) 先康一康模板题吧:CF 600E($Lomsat ...

随机推荐

  1. 记一次 Redisson 线上问题 → ERR unknown command 'WAIT' 的排查与分析

    开心一刻 昨晚和一个朋友聊天 我:处对象吗,咱俩试试? 朋友:我有对象 我:我不信,有对象不公开? 朋友:不好公开,我当的小三 问题背景 程序在生产环境稳定的跑着 直到有一天,公司执行组件漏洞扫描,有 ...

  2. 教你2种方法,将iOS设备通过MQTT协议连接到华为云物联网平台

    本文分享自华为云社区<如何将iOS设备通过MQTT协议连接到华为云物联网平台: Flutter和Swift两种方法>,作者: 张俭 . 前言 当今时代,物联网技术正逐步改变我们的生活和工作 ...

  3. 兴达易控Modbus转Profinet 网关连接 ACS510 变频器配置案例

    案例简介: 该案例为兴达易控Modbus转Profinet网关(XD-MDPN100)将ABB ACS510 变频器接入西门子 1200PLC.需要设备为西门子 PLC1200.ACS510 变频器. ...

  4. netstat命令输出详解

    netstat命令输出详解 1. 列出所有的TCP和UDP端口 2. 命令输出详解 Proto:协议名(tcp协议还是udp协议) recv-Q:网络接收队列,send-Q:网路发送队列 a. rec ...

  5. 开源.NetCore通用工具库Xmtool使用连载 - 扩展动态对象篇

    [Github源码] <上一篇> 介绍了Xmtool工具库中的图形验证码类库,今天我们继续为大家介绍其中的扩展动态对象类库. 扩展动态对象是整个工具库中最重要的一个设计.在软件开发过程中, ...

  6. 数据泵(impdb)导入Oracle分片的数据库dump文件

    数据泵(impdb)导入Oracle数据库 一.sqlplus登录目标数据库,创建导入的目录路径 #该目录要在导入的数据库本机建立,如果是docker就在容器内部创建 create directory ...

  7. MySQL innoDB 间隙锁产生的死锁问题

    背景 线上经常偶发死锁问题,当时处理一张表,也没有联表处理,但是有两个mq入口,并且消息体存在一样的情况,频率还不是很低,这么一个背景,我非常容易怀疑到,两个消息同时近到这一个事务里面导致的,但是是偶 ...

  8. FFmpeg: How To Convert MP4 Video To MP3 Audio?

       FFmpeg: How To Convert MP4 Video To MP3 Audio? Learn how to Convert an MP4 Video to MP3 Audio wit ...

  9. Rust WebAssembly 绑定输入类型(基于 Serde)

    前言 单位有个项目要共享前后端检查策略后端用的正好也是 Rust,但是 Rust 默认的 wasm-bindgen 包中提供的转换操作非常少,像 Vec<T> <=> Arra ...

  10. [AHOI2002] Kitty猫基因突变

    我们不妨将所有权值打到一棵树上,这很容易想到. 考虑暴力,如果我们选择了 \(w\) 个点,修改后我们会从叶子节点依次合并去计算贡献. 很显然我们可以动态规划维护. \(f[p][w][0/1/2]\ ...