【树剖求LCA】树剖知识点
不太优美但是有注释的版本:
#include<cstdio>
#include<iostream>
using namespace std;
struct edge{
int to,ne;
}e[1000005];
int n,m,s,ecnt,head[500005],dep[500005],siz[500005],son[500005],top[500005],f[500005];
void add(int x,int y) //加边
{
e[++ecnt].to=y;
e[ecnt].ne=head[x];
head[x]=ecnt;
}
void dfs1(int x) //构造树
{
siz[x]=1; //假设当前节点仅有一个儿子
dep[x]=dep[f[x]]+1; //当前节点深度=父亲节点深度+1
for(int i=head[x];i;i=e[i].ne) //遍历所有的子节点
{
int dd=e[i].to;
if(dd==f[x])continue; //如果是父节点,则略过
f[dd]=x; //那么确定x是当前节点的父亲
dfs1(dd); //向下遍历
siz[x]+=siz[dd]; //遍历完子树之后,加上子树的大小
if(!son[x]||siz[son[x]]<siz[dd]) //如果x节点重儿子未确定或者重儿子的子树比当前遍历节点的子树小
son[x]=dd; //更新重儿子
}
} void dfs2(int x,int tv) //求重链
{
top[x]=tv; //设置x所在重链顶为tv
if(son[x])dfs2(son[x],tv); //如果x有重儿子,那么随着这条重链走
for(int i=head[x];i;i=e[i].ne)
{
int dd=e[i].to;
if(dd==f[x]||dd==son[x])continue; //如果走到父亲或者走到重儿子(已经走过重儿子,避免重复),那么跳过
dfs2(dd,dd); //开启一条新链,链顶是其本身
}
}
int lca(int x,int y)
{
while(top[x]!=top[y]) //如果二者不在同一条重链上
{
if(dep[top[x]] >= dep[top[y]]) x=f[top[x]]; //选择所在重链的顶的深度较大的点向上跳,目的是防止跳过LCA
else y=f[top[y]];
}
return dep[x] < dep[y] ?x :y; //当二者在同一条重链上的时候,选择深度较浅的点即为lca }
int main()
{
scanf("%d%d%d",&n,&m,&s);
for(int i=1;i<n;++i)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs1(s);
dfs2(s,s);
for(int i=1;i<=m;++i)
{
int x,y;
scanf("%d%d",&x,&y);
printf("%d\n",lca(x,y));
}
}
比较优美但是没注释的版本:
#include<iostream>
#include<vector>
#include<cstdio>
#include<queue>
#include<map>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<set>
#include<cstring>
using namespace std;
typedef long long ll;
const ll INF=99999999;
const int N = 500010; int n,m,s; struct edge{
int to,ne; }e[N*2]; int top[N],siz[N],son[N],fa[N],dep[N];
int head[N],ecnt = 1; void add(int x,int y)
{
e[ecnt].to = y;
e[ecnt].ne = head[x]; head[x] = ecnt++;
} void dfs1(int x)
{
siz[x] = 1;
dep[x] = dep[fa[x]] + 1; for(int i = head[x];i;i = e[i].ne){
int t = e[i].to;
if(t == fa[x]) continue;
fa[t] = x; dfs1(t);
siz[x] += siz[t];
if(!son[x]||siz[son[x]] < siz[t])
son[x] = t;
}
} void dfs2(int x,int tp)
{
top[x] = tp; if(son[x])
dfs2(son[x],tp); for(int i = head[x];i;i = e[i].ne){
int t = e[i].to;
if(t == son[x]||t == fa[x]) continue; dfs2(t,t);
} } int lca(int x,int y)
{
while(top[x] != top[y]){
if(dep[top[x]] >= dep[top[y]]) x = fa[top[x]];
else y = fa[top[y]];
}
return dep[x] < dep[y] ?x :y;
}
int main()
{ scanf("%d%d%d",&n,&m,&s);
for(int i = 1;i < n;i++){
int x,y;
scanf("%d%d",&x,&y); add(x,y);
add(y,x);
}
dfs1(s);
dfs2(s,s); for(int i = 1;i <= m;i++){
int a,b;
scanf("%d%d",&a,&b); printf("%d\n",lca(a,b));
}
return 0;
}
树剖理解容易,需要注意的是题目如果给的是双向边,e数组需要开两倍于边数
【树剖求LCA】树剖知识点的更多相关文章
- 树链剖分 树剖求lca 学习笔记
树链剖分 顾名思义,就是把一课时分成若干条链,使得它可以用数据结构(例如线段树)来维护 一些定义: 重儿子:子树最大的儿子 轻儿子:除了重儿子以外的儿子 重边:父节点与重儿子组成的边 轻边:除重边以外 ...
- BZOJ1906树上的蚂蚁&BZOJ3700发展城市——RMQ求LCA+树链的交
题目描述 众所周知,Hzwer学长是一名高富帅,他打算投入巨资发展一些小城市. Hzwer打算在城市中开N个宾馆,由于Hzwer非常壕,所以宾馆必须建在空中,但是这样就必须建立宾馆之间的连接通道.机智 ...
- BZOJ-3881:Divljak (AC自动机+DFS序+树链求并+树状数组)
Alice有n个字符串S_1,S_2...S_n,Bob有一个字符串集合T,一开始集合是空的. 接下来会发生q个操作,操作有两种形式: “1 P”,Bob往自己的集合里添加了一个字符串P. “2 x” ...
- 【bzoj3362/3363/3364/3365】[Usaco2004 Feb]树上问题杂烩 并查集/树的直径/LCA/树的点分治
题目描述 农夫约翰有N(2≤N≤40000)个农场,标号1到N,M(2≤M≤40000)条的不同的垂直或水平的道路连结着农场,道路的长度不超过1000.这些农场的分布就像下面的地图一样, 图中农场用F ...
- 树链剖分 (求LCA,第K祖先,轻重链剖分、长链剖分)
2020/4/30 15:55 树链剖分是一种十分实用的树的方法,用来处理LCA等祖先问题,以及对一棵树上的节点进行批量修改.权值和查询等有奇效. So, what is 树链剖分? 可以简单 ...
- Bzoj 2588 Spoj 10628. Count on a tree(树链剖分LCA+主席树)
2588: Spoj 10628. Count on a tree Time Limit: 12 Sec Memory Limit: 128 MB Description 给定一棵N个节点的树,每个点 ...
- 【bzoj3083】遥远的国度 树链剖分+线段树
题目描述 描述zcwwzdjn在追杀十分sb的zhx,而zhx逃入了一个遥远的国度.当zcwwzdjn准备进入遥远的国度继续追杀时,守护神RapiD阻拦了zcwwzdjn的去路,他需要zcwwzdjn ...
- 浅谈求lca
lca即最近公共祖先,求最近公共祖先的方法大概有3种,其实是窝只听说过3种,这3种做法分别是倍增求lca,树剖求lca和tarjan求lca,但是窝只会前2种,所以这里只说前2种算法了. 首先是倍增求 ...
- tarjan,树剖,倍增求lca
1.tarjan求lca 思想: void tarjan(int u,int f){ for(int i=---){//枚举边 if(v==f) continue; dfs(v); //继续搜 uni ...
随机推荐
- jstree中json data 的生成
jstree中json data 的生成 jstree官网上给出的json数据格式是这样的: <span style="font-size:14px;">// A ...
- 牛客多校Round 10
咕咕咕.... 去烽火台和兵马俑了
- php第二十二节课
AJAX <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3 ...
- Oracle 回滚(ROLLBACK)和撤销(UNDO)
一.回滚(ROLLBACK)和撤销(UNDO) 回滚和前滚是保证Oracle数据库中的数据处于一致性状态的重要手段. 在9i版本以前 Oracle使用数据库中的回滚段来实现未提交数据或因系统故障导致实 ...
- python3.x Day2 购物车程序练习
购物车程序: 1.启动程序后,输入用户名密码后,如果是第一次登录,让用户输入工资,然后打印商品列表 2.允许用户根据商品编号购买商品 3.用户选择商品后,检测余额是否够,够就直接扣款,不够就提醒 4. ...
- Java 中 break和 continue 的使用方法及区别
break break可用于循环和switch...case...语句中. 用于switch...case中: 执行完满足case条件的内容内后结束switch,不执行下面的语句. eg: publi ...
- react入门-----(jsx语法,在react中获取真实的dom节点)
1.jsx语法 var names = ['Alice', 'Emily', 'Kate']; <!-- HTML 语言直接写在 JavaScript 语言之中,不加任何引号,这就是 JSX 的 ...
- 负载均衡之nginx+consul(自动更新路由)
前几篇先是记载了如何通过nginx配置服务负载均衡,后面记载了如何通过 ocelot 配置 服务负载均衡,分别介绍了用webapi注册服务以及配置文件注册服务,通过ocelot webapi + co ...
- Android第三方开源SeekBarCompat:音乐类播放器等APP进度条常用
Android第三方开源SeekBarCompat:音乐类播放器等APP进度条常用 Android平台原生的SeekBar设计简单,然而,比如现在流行的一些音乐播放器的播放进度控制条,如果直接使 ...
- MongoDB怎么快速的删除数据库数据?
我的mongodb里有10+数据库.现在需要重置这个环境,得到干净的没有数据的MongoDB.怎么快速安全的删除这些数据库数据呢? 记得首先备份你的数据库mongodump -o bakfolder ...