首页 > 其他分享 >树上倍增

树上倍增

时间:2024-02-01 10:25:02浏览次数:26  
标签:树上 int 32 vis 祖先 时间 倍增 节点


第2题     [模板] LCA 查看测评数据信息

模板题:求 LCA(最近公共祖先)。

以 1 号节点为树的根。


输入格式

第一行两个正整数 n, m,表示树的节点数和询问的个数。

接下来 n - 1 行,每行两个正整数 u, v,表示树上有一条连接节点 u 和 v 的边。

接下来 m 行,每行两个正整数 u, v,表示询问节点 u 和 v 的最近公共祖先。


对于 40% 的数据,满足 1 ≤ n, m ≤ 100。

对于 60% 的数据,满足 1 ≤ n, m ≤ 104,数据随机生成。

对于 100% 的数据,满足 1 ≤ n, m ≤ 105。


输出格式

输出共 m 行,每行一个整数表示答案。

输入/输出例子1

输入:

5 2

1 2

1 3

2 4

2 5

1 4

5 4


输出:

1

2

样例解释



 

解释1:https://www.luogu.com.cn/problem/solution/P3379 (第一篇题解详细解释,按深度做)

解释2:按时间戳做

#include <bits/stdc++.h>
using namespace std;

const int N=1e5+5, M=32;
int n, m, u1, v1, vis[N], f[N][M], cnt=0;
//vis[i]:节点i访问时间
//f[i][j]:从节点f,走2^j步,能到的点 vector<int> a[N]; void dfs(int u, int fa) //u:当前节点 fa:他的父亲节点 { vis[u]=++cnt; //访问时间 f[u][0]=fa; //u点走1步到父亲 for (int i=1; i<=20; i++) //2^20就行,看题目范围 f[u][i]=f[f[u][i-1]][i-1]; //拆两半,u节点跳2^i步=u节点跳2^(i-1)步+u节点跳2^(i-1)步=f [ f[u][i-1] ] [i-1] //意思是u的2^i祖先等于u的2^(i-1)祖先的2^(i-1)祖先
  //2^i = 2^(i-1) + 2^(i-1) for (int i=0; i<a[u].size(); i++) { int v=a[u][i]; if (v!=fa) dfs(v, u); } } int lca(int u, int v) { if (vis[u]>vis[v]) swap(u, v); //保证v访问时间是大的 if (u==v) return u; //别忘了加 for (int i=20; i>=0; i--)//从后往前推。原因如下
//从小向大跳,5≠1+2+4,所以我们还要回溯一步,然后才能得出5=1+4;而从大向小跳,直接可以得出5=4+1。这也可以拿二进制为例,5(101),从高位向低位填很简单,如果填了这位之后比原数大了,那我就不填,这个过程是很好操作的。 if (vis[f[v][i]]>vis[u]) //见下 解释1 v=f[v][i]; return f[v][0]; //距离u,v最近公共祖先还差一步 } int main() { scanf("%d%d", &n, &m); for (int i=1; i<n; i++) { scanf("%d%d", &u1, &v1); a[u1].push_back(v1); a[v1].push_back(u1); } dfs(1, 1); //这里定1号节点的父亲节点是自己 while (m--) { scanf("%d%d", &u1, &v1); printf("%d\n", lca(u1, v1)); } return 0; }

 解释1

 例如这张图,u,v最近公共祖先是三角型那里

有一些二分的思想(v点时间必须>u点时间,且>三角型点时间)

v点先尝试跳64步,发现不行,跳32步.....一直到跳16步,才可以比三角形大,也比u大(因为先跟遍历,所以那个点时间必然>u时间)

发现可行就抬高,然后最终就一定会在三角形点的下面一个点

 

标签:树上,int,32,vis,祖先,时间,倍增,节点
From: https://www.cnblogs.com/didiao233/p/18000670

相关文章

  • 树上差分
    洛谷3128思路要进行多次的树上某一段路径的加法操作,暴力做法时间复杂度较大,考虑差分。对树上路径的两个端点进行操作,在进行遍历的时候将路径的其他点的值还原,从而降低时间复杂度。注意思路来自董晓算法代码实现点击查看代码#include<bits/stdc++.h>usingnamespacestd;......
  • UOJ #33 树上 GCD
    套路地,对每个\(g\in[1,n-1]\)求出有多少对无序对\((u,v)\)满足\(g|f(u,v)\),然后就可以用一个倒序枚举的\(\mathcalO(n\logn)\)的容斥求出答案。考虑点分治,对每个经过分治中心\(c\)的\(u\rightsquigarrow\text{lca}(u,v)\rightsquigarrowv\)只需分两种......
  • 【学习笔记】部分树上算法(概念篇)
    本文包括:轻重链剖分(done)线段树合并(done)tobeupd:长链剖分DSUontree(树上启发式合并)点分治边分治LCT有待更新本文非例题代码大多未经过编译,谨慎使用本文本来只有重剖长剖dsu,但是发现不会写,另外几个甚至更简单就带歪了.jpgpart1轻重链剖分树剖是一类算法的总......
  • 1.24 两道树上问题的解法与思考
    内容过于简单,勿喷。1.1P6072Path(双log)选择两条简单路径,满足没有重合的点,且边权异或和之和最大。\(n\le10^5\)。我们可以把问题转化为选出一个\(u\),令在子树内选出两个点的异或和最大为\(f_u\),子树外为\(g_u\),那么我们需要求\(f_u+g_u\)的最大值。首先,通过DSUont......
  • CF-1184-E3-最小生成树+倍增+并查集
    1184-E3题目大意给定一个\(n\)个点,\(m\)条边的无向图,边带权。对于每条边,你需要找到最大值\(x\),使得把这条边的权值修改为\(x\)后能够出现在最小生成树上。Solution先把整个图的最小生成树弄出来,然后将边分为树边以及非树边来考虑:非树边:对于一个非树边连接了\(x\)和\(y\)的......
  • 优美的暴力——树上启发式合并(dsu on tree)
    dsuontree前言在我认为,这个并不能说单独列出来成为一个算法,更恰当的说,是一种思想、技巧。反正挺简单的,也很有趣(谁会拒绝一个优美的暴力呢),所以写篇笔记记录一手。dsu是什么dsu一般指“disjointsetunion”,即并查集。那么dsuontree也就是指树上的合并和查询操作。但是......
  • 洛谷 P9745 「KDOI-06-S」树上异或 题解
    Solution树形DP好题。PartI部分分类比下文为简单,我们称一个连通块的权值为连通块内点的异或和。考虑链的部分分,显然可以设\(f_{i}\)是前\(i\)个点所有断边方案的权值和,对于每个点枚举上一条断的边转移。令\(s_i=\bigoplus_{j=1}^{i}a_j\),则\(f_i=\sum_{j=0}^{i-1}(s......
  • CF-702-E-倍增
    702-E题目大意\(n\)个点,每个点有一条出边,边带权。给定整数\(k\)。求从每个节点出发经过\(k\)条边的路径上所有的边权和,以及最小的边权。Solution给定的图是基环树森林,从任意一个点出发无限走下去一定会进入环内。倍增板子题,这里不详细解释什么是倍增数组,具体的可以网上自行......
  • 【树上DP前导知识汇总】
    一、树的直径记录最长、次长,输出\(max(最长+次长)\)\(AcWing\)\(1072\)树的最长路径#include<bits/stdc++.h>usingnamespacestd;constintN=10010,M=N<<1;intn;//n个结点//链式前向星inth[N],e[M],w[M],ne[M],idx;voidadd(inta,intb,intc......
  • 【树上启发式合并】CF1709E XOR Tree
    XORTree\(\mathtt{TAGS}\):树上启发式合并+异或+贪心\(\mathtt{ESTIMATION}\):非常好的启发式合并题目First.如何去除\(0\)路径对于一条路径\(u\tov\),要使其不为\(0\)肯定是将\(\mathtt{LCA}(u,v)\)变为\(2^{30+x}\)最好,这样异或值的第\(30+x\)位一......