lca 学习笔记
定义
- 最近公共祖先简称 \(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 学习笔记的更多相关文章
- LCA学习笔记
写在前面 目录 一.LCA的定义 二.暴力法求LCA 三.倍增法求LCA 四.树链剖分求LCA 五.LCA典型例题 题目完成度 一.LCA的定义 LCA指的是最近公共祖先.具体地,给定一棵有根树,若结 ...
- 倍增LCA学习笔记
前言 "倍增",作为一种二进制拆分思想,广泛用于各中算法,如\(ST\)表,求解\(LCA\)等等...今天,我们仅讨论用该思想来求解树上两个节点的\(LCA\)(最近公共祖先 ...
- 倍增求LCA学习笔记(洛谷 P3379 【模板】最近公共祖先(LCA))
倍增求\(LCA\) 倍增基础 从字面意思理解,倍增就是"成倍增长". 一般地,此处的增长并非线性地翻倍,而是在预处理时处理长度为\(2^n(n\in \mathbb{N}^+)\ ...
- 树链剖分 树剖求lca 学习笔记
树链剖分 顾名思义,就是把一课时分成若干条链,使得它可以用数据结构(例如线段树)来维护 一些定义: 重儿子:子树最大的儿子 轻儿子:除了重儿子以外的儿子 重边:父节点与重儿子组成的边 轻边:除重边以外 ...
- LCA学习笔记(原洛谷文章)
本文原发布时间:\(\texttt{2022-05-21 14:11:52}\) 简介 最经公共祖先 \(\operatorname{LCA}(a,b)=c\),指的是在一棵树上节点 \(a\) 与 ...
- 算法学习笔记(5): 最近公共祖先(LCA)
最近公共祖先(LCA) 目录 最近公共祖先(LCA) 定义 求法 方法一:树上倍增 朴素算法 复杂度分析 方法二:dfs序与ST表 初始化与查询 复杂度分析 方法三:树链剖分 DFS序 性质 重链 重 ...
- kruskal重构树学习笔记
\(kruskal\) 重构树学习笔记 前言 \(8102IONCC\) 中考到了,本蒟蒻不会,所以学一下. 前置知识 \(kruskal\) 求最小(大)生成树,树上求 \(lca\). 算法详 ...
- 仙人掌&圆方树学习笔记
仙人掌&圆方树学习笔记 1.仙人掌 圆方树用来干啥? --处理仙人掌的问题. 仙人掌是啥? (图片来自于\(BZOJ1023\)) --也就是任意一条边只会出现在一个环里面. 当然,如果你的图 ...
- Day 4 学习笔记 各种图论
Day 4 学习笔记 各种图论 图是什么???? 不是我上传的图床上的那些垃圾解释... 一.图: 1.定义 由顶点和边组成的集合叫做图. 2.分类: 边如果是有向边,就是有向图:否则,就是无向图. ...
- 树上启发式合并(dsu on tree)学习笔记
有丶难,学到自闭 参考的文章: zcysky:[学习笔记]dsu on tree Arpa:[Tutorial] Sack (dsu on tree) 先康一康模板题吧:CF 600E($Lomsat ...
随机推荐
- 【译】.NET 8 拦截器(interceptor)
通常情况下,出于多种原因,我不会说我喜欢写关于预览功能的文章.我的大多数帖子旨在帮助人们解决他们可能遇到的问题,而不是找个肥皂盒或打广告.但是我认为我应该介绍这个 .NET 预览特性,因为它是我在 . ...
- 如何对MongoDB进行测试
一.环境搭建 关于环境搭建,最好的搭建方式,当然是脚本一键式搭建 我这里是centos6 x64版本的linux上进行构建,这个linux版本现在应该是大部分的主流服务器的标配版本 下面是安装脚本的编 ...
- [项目源码] JavaWeb校园宿舍管理系统
jsp校园宿舍管理系统源码,采用Servlet+JSP+MySQL.包含数据库文件,界面采用bootstrap,简洁大方. 项目导入eclipse后的目录结构如下: 关注下面公众号,下载源码
- 【接口自动化测试】Eolink Apilkit 安装部署,支持 Windows、Mac、Linux 等系统
Eolink Apikit 有三种客户端,可以依据自己的情况选择.三种客户端的数据是共用的,因此可以随时切换不同的客户端. 我们推荐使用新推出的 Apikit PC 客户端,PC 端拥有线上产品所有的 ...
- 地理探测器Geodetector下载、使用、结果分析方法
本文介绍Geodetector软件的下载方法,以及地理探测器分析的完整操作,并对其结果加以解读. 首先,我们介绍Geodetector软件的下载方法.进入软件官网,可以看到其中的第四个部分为软 ...
- 【.NET8】访问私有成员新姿势UnsafeAccessor(下)
前言 书接上回,我们讨论了在.NET8中新增的UnsafeAccessor,并且通过UnsafeAccessor访问了私有成员,这极大的方便了我们代码的编写,当然也聊到了它当前存在的一些局限性,那么它 ...
- 聊一聊 TLS/SSL
哈喽大家好,我是咸鱼 当我们在上网冲浪的时候,会在浏览器界面顶部看到一个小锁标志,或者网址以 "https://" 开头 这意味着我们正在使用 TLS/SSL 协议进行安全通信.虽 ...
- java数组的定义和使用规范
java数组 三种定义方式 1.数组类型[] 数组名字 = new 数组类型[数组长度] String[] str = new String[n]; //这里n代表数组的长度可变 //另外上面这种写法 ...
- 如何基于three.js(webgl)引擎架构,研发一套通过配置就能自动生成的3D机房系统
序: 这几年观察下来,大部分做物联网三维可视化解决方案的企业或个人, 基本都绕不开3D机房.包括前面也讲过这样的案例<使用webgl(three.js)创建自动化抽象化3D机房,3D机房模块详细 ...
- Pandas 读取Eexcel - 间隔N行,读取某列数据
间隔N行,读取某列数据 import pandas as pd def read_vertical(sheet_name, col_idx, gap): """ 竖着读数 ...