lca

【LN2014】LCA

眉间皱痕 提交于 2020-01-28 19:32:37
题目链接: https://www.luogu.com.cn/problem/P4211 题目大意:给定一棵有根树,对于 \(q\) 个询问 \(l, r, z\) , 求 \(\sum\limits_{l \leq i \leq r} {depth(Lca(i, z))}\) solution 考虑另一个问题:求出 \(\sum\limits_{1 \leq i \leq n} {dep[Lca(i, z)]}\) 此问题可以转化为 \(i\) 从 1 到 \(n\) , 分别把 1 到 \(i\) 路径上的所有点权值加 1, 再求 \(z\) 点的权值 不难发现 , 原问题可已转化为 \(\sum\limits_{1 \leq i \leq r} {dep[Lca(i, z)]} - \sum\limits_{1 \leq i \leq l - 1} {dep[Lca(i, z)]}\) , 可以把原问题分解成两个子问题并离线 , 从 1 到 \(n\) 分别用树剖修改 , 在此过程中算出子问题的答案 , 最后统计即可 时间复杂度: \(O(nlog^2n)\) code #include<bits/stdc++.h> using namespace std; template <typename T> inline void read(T &FF) { int RR = 1;

浅谈RMQ实现LCA

风流意气都作罢 提交于 2020-01-28 07:52:10
求两个点的LCA一共有四种方法 Tarjan,倍增,RMQ还有树链剖分(我也不会) 今天我们来学习如何用RMQ来实现LCA 首先我们要知道什么是RMQ(区间最值) 推荐一篇大佬的博客: https://www.cnblogs.com/YSFAC/p/7189571.html 好了,现在大家都知道了什么是RMQ,那么我们来看一下什么是LCA LCA是树上两个点的最近公共祖先 举个例子: 在这个图中,4和5的最近公共祖先就是2 5和6的最近公共祖先就是1 然后我们来看如何快速的求出两个点的最近公共祖先 首先是求出每个点的深度 这个操作用一个DFS维护即可,每个店的深度是其父亲的深度加1 void dfs(int x,int fa){ for(int e=head[x];e;e=nxt[e]){ d[to[e]]=d[x]+1; dfs(to[e],x); } return ; } 然后是维护f数组 f数组储存的是每个点向上跳2^j步所到达的点,并且我们可以确定这个点是唯一的,因为是树 然后来看如何维护 我们可以用递推的方法来求,时间复杂度是(nlogn)的 对于f[i][j] 是从x往上跳2^j步 可以推导出相当于是从x往上跳2^j-1步,然后再跳2^j-1步 也就是相当于从x往上跳2^j-1步之后,从那个点在往上跳2^j-1步 由此得出了我们的递推式: f[i][j]=f[f[i]

树上倍增:求LCA(u,v)

别等时光非礼了梦想. 提交于 2020-01-28 03:43:30
求LCA(u,v)的三种方法: 1:rmq+dfs()序 2:并查集+dfs() 3:树上倍增 然而听说1,2两种方法不怎么火热,3是目前最受欢迎的,故我就跟随大众潮流,学了树上倍增求 LCA(u,v) 的方法 树上倍增:利用了 rmq 的思想,首先定义一个 pre[i][j] 数组, pre[i][j] 表示 i 往上走 2^j 层所表示的父辈,根据倍增关系,我们可以得到: pre[i][j]=pre[pre[i][j-1]][j-1] ,然后我们利用 dfs() 处理每一个点的深度: depth [ u ] = depth [ fa ] + 1 并根节当前节点的深度,倍增u所能达到的点 代码: void dfs ( int u , int fa ) //求深度 { depth [ u ] = depth [ fa ] + 1 ; pre [ u ] [ 0 ] = fa ; for ( int i = 1 ; ( 1 << i ) <= depth [ u ] ; i ++ ) pre [ u ] [ i ] = pre [ pre [ u ] [ i - 1 ] ] [ i - 1 ] ; for ( int i = head [ u ] ; ~ i ; i = edge [ i ] . nex ) { int v = edge [ i ] . to ; if ( fa !=

树论讲解——最近公共祖先(lca)

冷暖自知 提交于 2020-01-27 18:48:51
最近公共祖先?! 有人肯定要问: 什么是最近公共祖先???!! 好那我们现在就来说说什么是最近公共祖先吧! 最近公共祖先有一个好听的名字叫——lca 这是一种算法,这个 算法 基于并查集和深度优先搜索。算法从根开始,对每一棵子树进行深度优先搜索,访问根时,将创建由根结点构建的集合,然后对以他的孩子结点为根的子树进行搜索,使对于 u, v 属于其某一棵子树的 LCA 询问完成。这时将其所有子树结点与根结点合并为一个集合。 对于属于这个集合的结点 u, v 其 LCA 必定是根结点。 对于有根树T的两个结点u、v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u、v的祖先且x的深度尽可能大。 怎么求两个已知点的LCA呢·? 有一个比较暴力的想法:先将这两个点的路径上的所经过的所有点,然后再从根节点向下找第一个分叉的点,这个点就是这两个点的最近公共祖先。 还有一个想法:先将两个深度不同的点转化成深度相同的点,然后再将这两个点一起向上跳,直到找到同一个点。         §  one。倍增法 何为倍增法? 倍增法就是我们先把深度不同的两个点转化成深度相同的点。然后再对这两个点同时倍增。 这种做法我们先用一个数组fa[x]【y】数组来存第x个节点的2^y的父亲节点。 这样我们就能在o(lg n)的时间内查询任意一个点的lca。 所以我们还是采用上面所述的那种做法

LCA的一些算法

人盡茶涼 提交于 2020-01-27 03:36:26
  LCA,就是求树上任意两点的最近公共祖先   (本题图片与代码均为Luogu3379)   方法我好像讲过一个,这次把主要的三个一起讲一讲   <1> 倍增(O(n log n))   我们先考虑最基本的LCA,记录每一个点的父节点和深度。   对于两个点x,y,先将它们调到同一高度(令dep[x]>dep[y],即把x向上移(dep[x]-dep[y])步即可,然后一起往上走就可以了。   这复杂度是O(nq)的,所以在此基础上优化。   用father[i][j]表示点j向上走2^i步时的点是多少(没有就是-1),然后每次上移只要走log n次即可。   预处理的话 father[i][j]]=father[i-1][father[i-1][j]];递推即可。   CODE #include<cstdio> #include<iostream> #include<cstring> using namespace std; const int N=500005,P=25; struct data { int to,next; }e[N*2+10]; int head[N*2+10],dep[N],father[P][N],i,j,n,m,x,y,root,k; inline void read(int &x) { x=0; char ch=getchar(); while

倍增法——解决LCA求树上两点最近公共祖先问题

柔情痞子 提交于 2020-01-26 17:21:09
在我们学过RMQ之后,我们知道了倍增法的原理,利用一个二的幂次来维护了每个以2为底数的区间长度。其实简单的说,就是一个合并问题,一个2可以有两个1组成,而一个4可以由两个2组成,以此类推。所以叫做倍增。倍增法通常能用以解决线性问题,譬如区间的最大值,当我们固定左端点的时候,当右端点不断向右移动的时候,不难发现,该区间的最大值也是单调不减的。 好了,一通乱讲之后该回归主题了。 现在,我们提出这样一个问题,我们是不是可以用倍增的方式来求得树上两个点的公共祖先呢? 譬如说,如图,有X和Y两个结点,我们想知道他俩的最近公共祖先呢? 给每个结点打上序号标记,方便之后的讲解: 于是乎,我们想知道X和Y的最近公共祖先,我们可以每次让X上升一格,让Y也上升一格,看看他俩的祖先是不是相同的。 【第一步】X会走到2,Y会走到3号结点,发现他两不相同,继续走; 【第二步】X走到1,Y走到1,他俩相同,退出,找到了最近公共祖先。 很容易发现一点,这样的做法是O(N)的。但是能保证其正确性(当X和Y的深度不相同的时候,肯定是要先将深度深的结点移动到深度浅的结点的深度上去) 其中,我们可以做出优化。因为我们可以知道,当向上走的步数越多,那么肯定就越会相同,我们类似就可以使用 倍增的方法 来进行维护了。 譬如说是这样的一棵树。 当我们先把X升高到和Y同深度的点,也就是X会转到点4上去(现在X在4号点的位置了)

bzoj 3626 [LNOI2014] LCA 题解

ⅰ亾dé卋堺 提交于 2020-01-26 07:12:22
博客观赏效果更佳 题意简述 给你一颗n<=1e5个节点树,1e5次询问,每次给你(l,r,z),求[l,r]区间内每个数和z在树上LCA的深度的和。对201314取膜。 注:根节点深度是1。 思路框架 (x,y)的LCA深度就是,把x到根点权+1,然后询问根到y点权和多少。 那么我们相当于,把[l,r]每个点到根点权都+1,然后询问根到z的点权和。差分做。 具体思路 差分做法:把一个询问(l,r,z)拆成(1,r,z)和(1,l-1,z)。(l,r,z)的答案显然就是(1,r,z)-(1,l-1,z)。这样我们要求的就是若干的前缀点的答案了。 我们把l-1和r都打上标记i从1到n遍历一下,不断在根到i的路径上点权+1。的如果某个点有标记,那么就把所有的z拿出来询问一遍,更新询问的答案。这样是O(nlogn+q)。 还有,对于每个标记,还要记录是l-1还是r,因为l-1的答案还要带一个负号。 代码 # include <bits/stdc++.h> using namespace std ; namespace Flandre_Scarlet { # define N 54444 # define mod 201314 # define F(i,l,r) for(int i=l;i<=r;++i) # define D(i,r,l) for(int i=r;i>=l;--i) #

求解LCA问题的几种方式

折月煮酒 提交于 2020-01-26 05:56:43
求解LCA问题的几种方式 这篇随笔讲解图论中LCA问题(最近公共祖先)的几种求解方式及实现方法。LCA问题属于高级图论,所以希望读者学习过初级图论,知道图的一些基本知识,并懂得深搜算法的实现方式。这样理解本篇博客将会快捷、舒适。 知识准备 理解LCA问题,理解节点深度是至关重要的,大家可以画一棵树。在一棵树中,所有的节点都有一个深度。根节点的深度是1,其他节点的深度可以用深搜遍历树来处理出来。这样,我们就可以通过深度数组来实现解决LCA问题的算法。 朴素LCA算法 朴素LCA算法的实现过程大约是这样:对于询问的两个点 \(x,y\) ,先判断两个点谁更深一些,然后把更深的点顺着它的父节点一步步往上提升,直到和 \(y\) 点的深度相等为止。然后同时提升 \(x,y\) 两点,直到这两个点变成同一个点,这时的那个点就是我们要求的LCA。 根据这个算法实现的特点,我们叫他“爬一爬”算法。这种算法极容易理解,但是奇慢无比。所以我们就不给代码了。 倍增LCA算法 刚刚提到的LCA朴素算法比较好理解,但是奇慢无比。所以我们推出了更高级一点的方法:倍增LCA。倍增思想其实是非常好用的一种优化思想,在算法优化中有很多使用实例。比如RMQ问题的暴力方法用倍增优化之后就变成了ST表(ST算法),比如LCA的爬一爬算法用倍增优化之后就变成了倍增LCA算法。 所谓倍增LCA,其实很好理解

天天爱跑步noip2016

穿精又带淫゛_ 提交于 2020-01-24 05:28:52
洛谷 跟隔壁的雨天的尾巴异常相似 ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 1.Analysis 某条路线对当前节点x有贡献时只可能是 x在s到t的路线上(即s到lca到t) 那么首先我们可以对从s到t的路线划分为 从s到lca(s,t) 以及从t到lca a.from s to lca 要有贡献 易得dep[s]-dep[x]=val[x](出现时间) 观察可得寻找s满足dep[s]=dep[x]+val[x]的数量 b.from lca to t 要有贡献 易得dep[s]+dep[x]-dep[lca]-dep[lca]=val[x](出现时间) 观察可得寻找s满足dep[s]-dep[lca]-dep[lca]=val[x]-dep[x]的数量 为了计算.我们规定lca被算在a部分中 2.实现 a.不断从叶子到lca累加 易联想到差分 b

C、Xor Path(LCA模板)

北慕城南 提交于 2020-01-24 01:43:54
题目: C、Xor Path 题解:先求根节点到每个结点的异或值,用pre[x],和pre[y]来表示,假设求结点x和结点y之间最短路径的异或值,pre[x]和pre[y]之间一定会有重合的部分,但是pre[x]^pre[y],就把相同的给抵消了。但是他们相同的一定会有离他们最近的公共结点 ,需要把这个点保存下来,就用这个点的异或值与他父亲的异或值相异或,就把这个点给保存了。 需要灵活运用倍增。 #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e5+10; ll bit[N]; int v[N],n; int step[N],f[N][30]; int Fa[N]; int ans[N];//根结点到x的异或值 vector<int>G[N]; /*********************************************************/ //以下是LCA倍增代码 /*********************************************************/ void init(){ for(int i = 1;i <= n;i++){ bit[i] = bit[i-1]+(1<<(bit[i-1])==i); } }