lca

LCA

梦想的初衷 提交于 2020-02-25 12:41:30
# pragma GCC optimize(2) # include <bits/stdc++.h> using namespace std ; typedef long long ll ; # define pi acos(-1.0) # define e exp(1.0) const ll maxn = 50 ; vector < ll > G [ maxn ] ; ll parent [ maxn ] ; ll depth [ maxn ] ; ll root ; //预处理出来父节点和深度 void DFS ( ll v , ll p , ll d ) //argument:当前节点 父节点 当前节点的深度 { depth [ v ] = d ; parent [ v ] = p ; ll i , u ; for ( i = 0 ; i < G [ v ] . size ( ) ; i ++ ) { u = G [ v ] [ i ] ; if ( u != p ) DFS ( u , v , d + 1 ) ; } return ; } ll LCA ( ll u , ll v ) { if ( u == v ) return u ; //先让u,v上升到同样高度 if ( depth [ u ] < depth [ v ] ) //每次让u向v看齐 swap ( u ,

最近公共祖先(LCA)---tarjan算法

余生长醉 提交于 2020-02-24 23:17:52
LCA(最近公共祖先).....可惜我只会用tarjan去做 真心感觉tarjan算法要比倍增算法要好理解的多,可能是我脑子笨吧略略略 最近公共祖先概念 : 在一棵无环的树上寻找两个点在这棵树上深度最大的公共的祖先节点,也就是离这两个点最近的祖先节点。 最近公共祖先的应用:求解两个有且仅有一条确定的最短路径的路径     举个例子吧,如下图所示 4 和 5 的 最近公共祖先是2 , 5 和 3 的 最近公共祖先 是 1 , 2 和 1 的 最近公共祖先 是 1 。      这就是最近公共祖先的基本概念了,那么我们该如何去求这个最近公共祖先呢? Tarjan介绍:   通常初学者都会想到最简单粗暴的一个办法:对于每个询问,遍历所有的点,时间复杂度为 O(n*q), 那么这个复杂度当然也就呵呵了。     so, 我们有求解LCA的特殊算法:Tarjan /DFS+ST/倍增 (因为我不会,所以不喜欢)     后两个算法都是在线算法,也很相似,时间复杂度在 O(logn)~O(nlogn) 之间,我个人认为较难理解。     有的题目是可以用线段树来做的,但是其代码量很大,时间复杂度也偏高,在 O(n)~O(nlogn) 之间,优点在于也是 简单粗暴 。 tarjan属于离线算法,所谓的离线算法就是说我们需要将所有的询问都读入后一次性输出

P3348 [ZJOI2016]大森林(LCT)

♀尐吖头ヾ 提交于 2020-02-24 16:29:19
Luogu3348 BZOJ4573 LOJ2092 题解 对于每个 \(1\) 操作建一个虚点,以后的 \(0\) 操作都连在最近建好的虚点上。这样每次整体嫁接的时候,直接把这个虚点断掉它原来的父亲,再 \(link\) 过去就可以了 求在 \(x\) 位置的两点之间距离 , 只要之前的换点加点操作完成 , 就可以计算 , 而且也要马上计算 , 之后树的形态就又要变了 , 这样保证了复杂度 关于代码 : 先按思路把离线的事件处理出来 然后发现这种离线的利用虚点的转移只能用 \(LCT\) 维护 注意 : 只有实点才算 \(size\) , 并统计到路径长度中去 , 利用的虚点不算 \(size\) 还有 \(LCT\) 上不 \(makeroot\) 时求 \(LCA\) 的求法 注释重构于 19.3.29 #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #define debug(...) fprintf(stderr,__VA_ARGS__) #define Debug(x) cout<<#x<<"="<<x<<endl using namespace std; typedef long long LL; const int INF=1e9+7; inline LL read(

LCA最近公共祖先(Tarjan离线算法)详解(转)

只谈情不闲聊 提交于 2020-02-21 04:55:15
转自 https://www.cnblogs.com/ECJTUACM-873284962/p/6613379.html 首先是最近公共祖先的概念(什么是最近公共祖先?):     在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先,就是两个节点在这棵树上 深度最大 的 公共 的 祖先节点 。     换句话说,就是两个点在这棵树上 距离最近的公共祖先节点 。     所以LCA主要是用来处理当两个点仅有 唯一一条 确定的最短路径时的路径。     有人可能会问:那他本身或者其父亲节点是否可以作为祖先节点呢?     答案是肯定的,很简单,按照人的亲戚观念来说, 你的父亲也是你的祖先 ,而LCA还可以 将自己视为祖先节点 。     举个例子吧,如下图所示 4 和 5 的 最近公共祖先是2 , 5 和 3 的 最近公共祖先 是 1 , 2 和 1 的 最近公共祖先 是 1 。      这就是最近公共祖先的基本概念了,那么我们该如何去求这个最近公共祖先呢?     通常初学者都会想到最简单粗暴的一个办法:对于每个询问,遍历所有的点,时间复杂度为 O(n*q) ,很明显, n和q一般不会很小 。     常用的求LCA的算法有:Tarjan/DFS+ST/倍增     后两个算法都是在线算法,也很相似,时间复杂度在 O(logn)~O(nlogn) 之间

【比赛】百度之星2017 初赛Round A

杀马特。学长 韩版系。学妹 提交于 2020-02-13 08:46:45
第一题 题意:给定多组数据P,每次询问P进制下,有多少数字B满足条件:只要数位之和是B的倍数,该数字就是B的倍数。 题解:此题是参考10进制下3和9倍数的特殊性质。 对于10进制,ab=10*a+b=9*a+(a+b),所以9的约数都有此性质。 对于P进制,ab=p*a+b=(p-1)a+(a+b),所以p-1的约数都有此性质。 对于P,计算P-1的约数个数即为答案。 ★第二题 题意:给定L的关系(L<=10^5),关系表示为xi=xj或xi≠xj,将数据按顺序分为若干组(相邻在同一组),每组不满足所有条件,去掉最后一个条件后满足所有条件,求组数。 题解: 一种方法是每次倍增一组个数,对于组内跑bfs或者并查集,最后对组内不等关系判互恰,倍增结合二分。 正解O(n log n)。 相等关系具有传递性,可以用并查集在线维护。 不等关系不具有传递性,用平衡树维护每个点的不等关系。 对于相等关系 ,若在同一集合跳过,若在对方平衡树内则清空退出,否则合并双方集合。 合并使用启发式合并,就是大的并入小的,并修改对应不等关系平衡树处的表示。 对于不等关系 ,若在同一集合则清空退出,否则加入对方平衡树。 清空退出:用到的点加入栈,清空时只清栈内平衡树和栈内父亲,保证复杂度。(vis时间戳也可以优化常数) 记得开双倍数组。 #include<cstdio> #include<cstring>

LCA最近公共祖先模板代码

China☆狼群 提交于 2020-02-13 06:21:04
vector模拟邻接表: 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<vector> 6 #include<queue> 7 #define eps 1e-8 8 #define memset(a,v) memset(a,v,sizeof(a)) 9 using namespace std; 10 typedef long long int LL; 11 const int MAXL(1e4); 12 const int INF(0x7f7f7f7f); 13 const int mod(1e9+7); 14 int dir[4][2]= {{-1,0},{1,0},{0,1},{0,-1}}; 15 int father[MAXL+50]; 16 bool is_root[MAXL+50]; 17 bool vis[MAXL+50]; 18 vector<int>v[MAXL+50]; 19 int root; 20 int cx,cy; 21 int ans; 22 int Find(int x) 23 { 24 if(x!=father[x]) 25 father[x]=Find(father[x]); 26 return father[x];

The North American Invitational Programming Contest 2016 - Tourists ( LCA )

天涯浪子 提交于 2020-02-12 20:30:16
题意 给出一棵n个点,n-1条边的树。现在计算所有标号为x到y的距离之和(满足y>x且y是x的倍数) 思路 关于树上任意两点距离之和,一开始想到树形dp,可树形dp,是对每条边,求所有可能的路径经过此边的次数,是求出边两端的点数,这条边被经过的次数就是两端点数的乘积。 但是该题对计算的距离加了限制(y>x且y是x的倍数),显然不能用树形dp来做了。 接下来想到图论部分的算法,想处理出来两点之间的距离,也就是最短路,但是n=2e5,跑n遍dijkstra或者n遍spfa在复杂度上肯定会T,故不可行。 队友提到LCA(最近公共祖先),随意定义一个根root,用LCA倍增法预处理出来每个点到根的深度数组dep[],然后找两个点的最近公共祖先lca,那么这两点间的距离即 dep[i] + dep[i*j] - dep[lca] * 2 + 1 (根据题意,距离是这两点间边的个数+1) 代码是kuangbin的LCA倍增法板子改了一波。 AC代码 #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <cstdlib> #include <iostream> #include <vector> #include <queue> using namespace std; const

POJ3728 THE MERCHANT LCA RMQ DP

旧巷老猫 提交于 2020-02-12 20:25:58
题意简述:给定一个N个节点的树,1<=N<=50000 每个节点都有一个权值,代表商品在这个节点的价格。商人从某个节点a移动到节点b,且只能购买并出售一次商品,问最多可以产生多大的利润。 算法分析:显然任意两个城市之间的路径是唯一的,商人有方向地从起点移动到终点。询问这条路径上任意两点权值之差最大为多少,且要保证权值较大的节点在路径上位于权值较小的节点之后。 暴力的方法是显而易见的,只要找到两个点的深度最深的公共祖先,就等于找到了这条路径,之后沿着路径走一遍即可找到最大的利润,然而无法满足50000的数据规模。 首先考虑高效寻找LCA(公共祖先)的方法。记录ance[i][j]为节点i向上走2^j步到达的某个祖先。可以简单地列出方程 ance[i][j]=ance[ance[i][j-1]][j-1];于是找到了高效构建的方法。 每次寻找LCA 首先将两个节点通过swim(a,b)函数转移到同一深度,然后每次找一个最小的j使得ance[a][j]==ance[b][j] 之后将节点a赋值为ance[a][j-1] 直到j=0就找到了两者的LCA 现在我们已经找到了高效寻找LCA的方法,假设我们知道节点a到LCA的最小值minp[],LCA到节点b的最大值maxp[], 以及买卖地点全在LCA之前可以获得的最大利润maxi[] 以及买卖地点全在LCA之后可以获得的最大利润maxI[]

「LCA + 树上差分」[USACO15DEC]最大流Max Flow

孤街醉人 提交于 2020-02-10 05:00:55
[USACO15DEC]最大流Max Flow 原题链接 最大流Max Flow 题目大意 给你 \(n - 1\) 条边,再给你 \(m\) 个操作,每次操作两个数 \(u, v\) 表示, \(u, v\) 的最短路径上每个点都加上 \(1\) 题目题解 树上差分的经典题 (一直想学树上差分,今天终于会了quq),我们修改时,使 \(power_u\) ++ , \(power_v\) ++, \(power_{lca(u,v)}\) --, \(power_{f[lca(u,v)][0]}\) -- 就可以了 接下来就是代码..xx //#define fre yes #include <cstdio> #include <cstring> #include <iostream> int n, m; const int N = 50005; int head[N << 1], to[N << 1], ver[N << 1]; int f[N][22], lg[N], depth[N], power[N]; int tot; void addedge(int x, int y) { ver[tot] = y; to[tot] = head[x]; head[x] = tot++; } void dfs(int x, int fa) { depth[x] = depth[fa]

[BJWC2018]Border 的四种求法(后缀自动机+链分治+线段树合并)

◇◆丶佛笑我妖孽 提交于 2020-02-09 10:04:10
题目描述 给一个小写字母字符串 S ,q 次询问每次给出 l,r ,求 s[l..r] 的 Border 。 Border: 对于给定的串 s ,最大的 i 使得 s[1..i] = s[|s|-i+1..|s|], |s| 为 s 的长度。 题解 这题的描述很短,给人一种很可做的假象。 暴力1:每次对区间lr做一次KMP,求出border数组,复杂度nq。 暴力2:构建后缀自动机,用线段树合并维护出right集合考虑到两个串的最长后缀为他们在parent树上的LCA的len,所以我们可以在parent树上跳father,相当于枚举LCA,那么如果有匹配的点,则一定满足: i-len[LCA]+1<=l => i=len[LCA]<l => i<l+len[LCA] i>=l&&i<r (i为我们要求的点,l为询问的左端点,LCA为i和r的LCA) 所以我们只需要在线段树上查子树内查询满足上述条件的最大的i就可以了,复杂度最好qlogn,最差qn。 这两种暴力好像差不多。。 我们观察到第二种暴力它的瓶颈在于枚举LCA,但查询只需要一个log,我们可以想一些办法把复杂度均摊一下。 链分治 这就是这道题的重头戏,它用到了一个重要的性质,我们将一棵树重链剖分之后,从根到任意一点的路径上,轻重链切换的次数是不超过log的。 然后我们就可以用它搞一些事情。 比如说这道题