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 ...
随机推荐
- [htmlayout] flow布局
css { flow:xxxx.... } flow: vertical --------------------- 默认值, 块元素的所有子元素从上到下. flow: horizontal ---- ...
- Solution Set -「ARC 116」(in progress)
「ARC 116A」Odd vs Even Link. 看 \(n\) 有多少 \(2\) 因子. // Problem: A - Odd vs Even // Contest: AtCoder - ...
- Solution -「ZJOI 2014」力
Descrption Link. 对于每一个 \(i\),求出: \[\sum_{j=1}^{i-1}\frac{a_{j}}{(i-j)^{2}}-\sum_{j=i+1}^{n}\frac{a_{ ...
- 基于Python的HTTP代理爬虫开发初探
前言 HTTP代理爬虫在爬取网页数据时,使用Python程序模拟客户端请求,同时使用HTTP代理服务器来隐藏客户端的真实IP地址.这样可以有效防止在爬取大量网页数据时被目标网站封禁IP地址. 以下是基 ...
- u-boot启动流程
U-Boot(Universal Bootloader)是一个通用的开源引导加载程序,通常用于嵌入式系统中,负责引导操作系统或加载 Linux 内核等任务.U-Boot的启动流程可以概括为以下几个关键 ...
- 其它——CGI,FastCGI,WSGI,uWSGI,uwsgi一文搞懂
文章目录 CGI, FastCGI, WSGI, uWSGI, uwsgi一文搞懂 一 CGI 二 FastCGI 三 WSGI 四 uWSGI 五 uwsgi CGI, FastCGI, WSGI, ...
- Redis——Redis面试题
文章目录 概述 什么是Redis Redis有哪些优缺点 为什么要用 Redis /为什么要用缓存 为什么要用 Redis 而不用 map/guava 做缓存? Redis为什么这么快 数据类型 Re ...
- 前端三件套系例之JQuery——JQuery动画效果、JQuery插件、
文章目录 1 JQuery动画效果 1. 基本效果 2. 滑动效果 3 淡入淡出效果 4 自定义动画 5 动画控制 6 设置 7 事件 7-1 常用事件 7-2 事件绑定 7-3 移除事件 7-4 阻 ...
- Keycloak 创建和修改自定义用户信息
前言 公司在用 Keycloak 作为认证服务器,之前在系统数据库里存的,后来想了想是不是可以在 Keycloak 中存.在网上找的方法大多都是通过 admin 接口去改,但这种方法就需要两种解决方案 ...
- AT通讯总结(56K猫调制解调器Modem)型号I-56EM
1.关闭流控RTS与DTR AT&D0&K0\r\n 2.保存到非易失性存储 AT&W\r\n 3.向800001音频拨号 ATDT800001\r\n 4.接听 ATA\r\ ...