lca

花式求LCA

我只是一个虾纸丫 提交于 2019-12-02 23:34:39
设树上有两点x、y,要求他们的lca(最近公共祖先) 1、倍增求LCA:   先预处理出树上每个点的向上2^k的祖先。   再看x、y:先把深度深的倍增跳到和深度浅的一样的深度,判断是否在同一点:是,该点即为lca;不是,就将两点一起倍增向上跳到最高的不同的两点,它们的父亲就是lca。   正确性:数可用二进制表示。 2、RMQ求LCA:    https://www.luogu.org/blog/hicc0305/solution-p3379   正确性:从f小的点x到f大的点y,经过了:x的一部分(或全部)子树,x到lca的路径,lca到y的路径上的点的一部分(或全部)子树、lca到y的路径。显然,其中深度最浅的只能是lca。 3、Tarjan求LCA:    https://www.cnblogs.com/abc2237512422/p/9832468.html   正确性:由两点间路径的唯一性可知,每个遍历过的点所在并查集的祖宗就是当前点和这个遍历过的点的lca。(假设先遍历了x,若当前遍历到y,一定是从x回溯到了x和y的lca后再到了y,由于并查集是在回溯时为维护的,当x回溯到lca后x所在并查集就会并到lca所在的并查集,但由于lca还没有回溯到它的父亲,所以它的并查集还没有与它的祖先合并) 4、树链剖分求lca:  

LCA cogs 2450 2048 1588

我怕爱的太早我们不能终老 提交于 2019-12-02 19:18:22
t1 2450距离 链接: http://cogs.pro:8081/cogs/problem/problem.php?pid=vSNNNVqga 【题目描述】 在一个村子里有N个房子,一些双向的路连接着他们。人们总喜欢问这个“如果1想从房子A走到房子B有多远?”这个通常很难回答。但幸运的是在这个村里答案总是唯一的,自从道路修建以来这只有唯一的一条路(意思是你不能去一个地方两次)在每两座房子之间。你的工作是回答所有好奇的人。 【输入格式】 输入文件第一行有两个数n(2≤n≤10000)和m(1≤m≤20000),即房子数和问题数。后面n-1行每行由3个数构成i,j,k,由空格隔开,意思是房子i和房子j之间距离为k(0<k≤100)。房子以1到n标记。 下面m行每行有两个不同的整数i和j,你需要回答房子i和房子j之间的距离。 【输出格式】 输出有n行。每行表示个一个问题的答案。 【样例1】 输入样例1: 3 2 1 2 10 3 1 15 1 2 2 3 输出样例1: 10 25 【样例2】 输入样例2: 2 2 1 2 100 1 2 2 1 输出样例2: 100 100思路:一道带边权的LCA,tarjan离线算法即可,将query读进来作为一种结构,同边一样记录,dfs的时候同时维护一个栈,栈元素的x为查询的from,i为边序号,维护这个栈即可,同时  距离dis[x

[机房测试]10.28

爱⌒轻易说出口 提交于 2019-12-02 16:44:25
[机房测试]10.28 10.26日原题大战,不是有手就能AK吗?,跳过 欢迎转载ssw02的博客: https://www.cnblogs.com/ssw02/p/11755483.html 地精部落 连接传送门: T2 模拟一下即可。在画画图找找性质之后发现,由于可以无线次数交换,其实我们要求的可以分为3种情况,记录每种字符出现的次数外加统计特判即可。 分为 两偶数 一奇一偶 两奇数 即可 , 具体看代码 #include<bits/stdc++.h> using namespace std ; inline int read(){ int s=0 ; char g=getchar() ; while(g>'9'||g<'0')g=getchar() ; while( g>='0'&&g<='9' )s=s*10+g-'0',g=getchar() ; return s; } inline int getc(){ char g=getchar() ; while( g>'z'||g<'a')g=getchar() ; return (int)g-'a'+1 ; } int cnt[30] , N , M , T ; void clear(){ memset( cnt , 0 , sizeof(cnt) ) ; } void work1(){ int opt = false ;

[LNOI2014] LCA

我怕爱的太早我们不能终老 提交于 2019-12-02 16:18:54
题目链接: 题目链接 题目分析 考虑暴力 枚举每个询问,从 \(l\) 到 \(r\) 依次把每个节点抓出来和 \(z\) 求 \(LCA\) 并累加 \(dep\) ,复杂度 \(O(n^2log(n))\) 考虑怎么优化,有两种入手点 一次性快速求出一大堆 \(LCA\) (不会,告辞) 或者能集中统计 \(dep\) 走后面一条路吧,前面一条是什么鬼啊不会啊 \(QAQ\) 假装我们不会倍增不会树剖不会 \(tarjan\) 不会一些奇奇怪怪的做法求 \(LCA\) 给我们两个点,我们要怎么求 \(LCA\) ? 一种可行的方案是一个点 \(u\) 跳到根节点,沿途染色并点权 \(+1\) ,那么另一个点 \(v\) 向上跳,遇到第一个染过色的点时,这个点就是他们的 \(LCA\) ,并且从 \(v\) 到根节点的路径点权和就是 \(LCA\) 的深度。 考虑对于每一个询问,我们仍然遍历所有的询问点,并暴力跳到根节点,沿途点权 \(+1\) ,最后跳 \(z\) 并统计此次询问答案。 恭喜我们把 \(O(n^2log(n))\) 的算法优化到 \(O(n^3)\) 考虑继续优化,显然从每一个询问点到根节点增加点权的操作可以用树剖的链加单次 \(O(log^2(n))\) 处理,每次询问后清空 \(segT\) ,得到新暴力 \(O(n^2log^2(n))\)

最近公共祖先 LCA

99封情书 提交于 2019-12-02 12:29:44
原创建时间:2018-08-07 14:08:52 两个结点找共同的爸爸 LCA 的概念 在 图论 和 计算机科学 中, 最近公共祖先 (英语:lowest common ancestor)是指在一个 树 或者 有向无环图 中同时拥有v和w作为后代的最深的节点。 ——Wikipedia 看不懂没关系 简单的来说,就是两个节点v和w的最近的祖先节点 如下图 6和7的LCA是2,3和7的LCA是1 LCA 的求法 暴力求解 让他们一步一步往上爬,直到相遇 节点背着那重重的编号呀 一步一步地往上爬 ——《蜗牛与黄鹂鸟》 显然,这样的算法会 T到飞起 所以我们要使用 倍增 优化 倍增求解 所谓倍增,就是按2的倍数来增大,也就是跳 1、2、4 、8 、16、32 ... 但是在这里,我们要考虑 开倒车 从大到小跳 因为 如果我们从小到大跳,就会出现要「回溯」的情况,因为我们不一定能精准地跳,而从大到小跳可以避开这种情况 图源cnblogs 对于上面这一棵更复杂的树,我们考虑17和18的LCA 17 ->(跳4) 3 18 ->(跳4) 5 ->(跳1) -> 3 是不是快多了,跳的次数大大减小 时间复杂度 \(O(nlogn)\) LCA 的代码 & 实现流程 实现流程 首先我们要记录各个点的深度 \(depth[\ ]\) 和它们 \(2^i\) 级的祖先 \(father[\ ][\ ]

洛谷P3884《[JLOI2009]二叉树问题》

霸气de小男生 提交于 2019-12-02 12:29:31
原创建时间:2018-08-08 16:31:55 不用倍增的 almost裸的LCA 题目描述 如下图所示的一棵二叉树的深度、宽度及结点间距离分别为: 深度:4 宽度:4(同一层最多结点个数) 结点间距离: ⑧→⑥为8 (3×2+2=8) ⑥→⑦为3 (1×2+1=3) 注:结点间距离的定义:由结点向根方向(上行方向)时的边数×2, 与由根向叶结点方向(下行方向)时的边数之和。 图片来自洛谷 Input / Output 格式 & 样例 输入格式 输入文件第一行为一个整数n(1≤n≤100),表示二叉树结点个数。接下来的n-1行,表示从结点x到结点y(约定根结点为1),最后一行两个整数u、v,表示求从结点u到结点v的距离。 输出格式: 三个数,每个数占一行,依次表示给定二叉树的深度、宽度及结点u到结点v间距离。 输入输出样例 输入样例: 10 1 2 1 3 2 4 2 5 3 6 3 7 5 8 5 9 6 10 8 6 输出样例: 4 4 8 解题思路 树的深度可以取 \(max\) { \(depth[i]\) } 树的宽度可以在取深度的时候拿一个桶记录下来,再循环取一遍 \(max\) 两点之间的距离可以先求 \(LCA\) ,再用一个公式算出来 \[distance = (depth[u] - depth[lca]) \times 2 + depth[v] -

NOIP2016---天天爱跑步

烂漫一生 提交于 2019-12-02 11:38:55
题目链接 ( https://www.luogu.org/problem/P1600 ) sol: 这是一个比较好的题,值得一做。首先这个不是一个普通的链覆盖问题,因为在某一条路径上每走一步,权值(即时间)就会变化 \(1\) 。那么现在就考虑要设定一个标准权,使得每一条路径拥有的那个权值固定,每一个点的权也固定。显然对于向下的路径,这个标准权就是深度;向上的即把深度倒过来。把时间和它作差即可。这样问题就转化为对于一个点 \(u\) ,设它的做差后的值为 \(val_u\) ,现在要统计所有路径权值为 \(val_u\) ,且经过 \(u\) 的路径条数。考虑处理出 \(dfs\) 序,用树状数组维护做差分即可。 这个做法挺难写的。首先拆路径时一定要仔细讨论各种情况,其次处理 \(lca\) 向下的第一个节点也极容易出错。 #include <bits/stdc++.h> #define mp make_pair #define pb push_back #define X first #define Y second using namespace std; inline int read(){ int x=0;char c=getchar(); while(c<'0'||c>'9') c=getchar(); while(c>='0'&&c<='9') x=(x<<1)+(x<

初学虚树

我怕爱的太早我们不能终老 提交于 2019-12-02 09:13:25
今天考了个模拟赛,虚树的题我拿 \(LCT\) 粗暴卡过,然后被各路神仙疯狂嘲讽,然后就奋发图强,来学了个虚树。 虚树的概念 先放一个例题吧: [SDOI 2011]消耗战 例题单个询问的树形DP可以说是非常简单了,但是多个询问就会 \(GG\) ,于是我们痛定思痛,发现其实我们并不需要整棵树来转移,我们只需要得到每一个目标节点和他们的 \(LCA\) 所构成的树即可,而树的边权即为树上两点在原树上的路径中的最小值。 那么像这种由目标节点和他们的 \(LCA\) 所构成的树即为虚树。 虚树的构造 既然知道了啥是虚树,那么就让我们再来看看虚树应该怎么构造吧。 其实虚树的构造过程类似 \(dfs\) 。 我们先整个类似 \(dfs\) 的栈,这个栈里存的只有虚树上需要的节点。 那么对于一个新加入的目标节点 \(x\) (显然目标节点要按 \(dfs\) 序排序再加入),我们分两种情况讨论: \(x\) 在以 \(stk[top]\) 为根的子树中 \(x\) 不在以 \(stk[top]\) 为根的子树中 第一种情况很简单,直接把 \(x\) 加入栈即可。 让我们来具体研究一下第二种情况: 我们找到次栈顶结点 \((stak[top-1])\) 与 \(x\) 的 \(LCA\) ,我们只需要一直把次栈顶与 \(LCA\) 比较,若 \(dep[stk[top-1]]>dep[LCA]

[LNOI2014]LCA

旧城冷巷雨未停 提交于 2019-12-02 08:46:48
[LNOI2014]LCA 这道题难道不是数据结构水题吗 逃) 首先想到一个i点的 \(dep\) 相当于根节点走到i 进过的点之和,所以这个点对答案的贡献就是从这个点走到根节点。因此我们可以转换为当前节点 \(i\) 到根节点所经过的所有节点权值++。 显然可以用线段树+树链剖分的数据结构进行优化 对于每一个询问 建一颗权值线段树 存储当前区间 \(sum\) , \(sum\) 存储的就是 \(sum\) 然后我们需要想到用 类似于前缀和的思想来进行优化 例如 \(l=2 ,r=4 ,z=4\) 我们可以用前缀和的思想进行优化 计算 \(1到4\) 的 \(sum\) \(-1到1的sum\) 就可以得到答案了 想到了前缀和 想到了树剖+线段树 如果每一次查询 你都要新建两次树 那就会GG! 因此我们又想到了莫队的思想,将询问区间的 \(l,r\) 排序(因为询问区间为 \(1到l\) , \(1到r\) ,左区间都是一样的,所以我们可以将l,r一起排序),然后一个一个的加点 这样就可以不用每一次询问都新建一颗树了 #include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #define int long long using namespace std; const int

tarjan求LCA学习笔记

允我心安 提交于 2019-12-02 07:53:59
tarjan求LCA学习笔记 其实,实质就是离线处理询问, 我们可以将所有询问看成类似于这样的样子, (图之后再补上) 每次提高LCA,当两点恰好连通时,两点之间最高的点就为LCA。 具体实施: 每次dfs搜索所有子树,搜索完后将子树内所有点的父亲标为祖辈中连通的最高点,搜到询问点时,若另一点已经搜过,LCA就为它祖辈中连通的最高点。 (图之后再补上) void tarjan(int x,int fa){ int v; for(int i=head[x];i;i=e[i].nxt) if(e[i].to!=fa) tarjan(e[i].to,x),f[e[i].to]=x; for(int i=head2[x];i;i=q[i].nxt){ v=q[i].to; if(book[v]) lca[q[i].w]=getf(v); } book[x]=1; } for(int i=1;i<=m;++i){ t1=read(),t2=read(),que[i].x=t1,que[i].y=t2; if(t1==t2){lca[i]=t1; continue;} add(t1,t2,i,q,head2),add(t2,t1,i,q,head2); } tarjan(1,0); 来源: https://www.cnblogs.com/ljk123-de-bo-ke/p/11736090