LCA模板
LCA,也就是最近公共祖先。
一般有三种写法。
树上倍增LCA,先序DFS(欧拉序)+RMQ(ST表),DFS序+并查集。
一、树上倍增
首先是树上倍增LCA
注意这里N = ceil(n) 也就是n个点向下取整。
首先初始化father数组
这里father数组代表的意思是father[i][j]也就是i点到树上i+2^j上的祖先,所以当j = 0的时候,就是自己的最近祖先。
又因为树上有father[i][j]的祖先是father[i][j-1]的祖先的祖先,所以有father[i][j] = father[ father[i][j-1] ][j-1]成立。
void update(int cur, int fa) { father[cur][0] = fa; for(int i = 1; i <= N; ++i) father[cur][i] = father[ father[cur][i-1] ][i-1]; }
有一种模板使用了dfs与求father数组相结合的比如这样
struct node { int to, weight; node(int a, int b) { to = a; weight = b; } }; void dfs(int u) { for(int i = 0; i < G[u].size(); ++i) { int v = G[u][i].to; if(!vis[v]) { depth[u] = depth[v]+1; vis[v] = 1; //dis[v] = dis[u]+G[u][i].weight; //计算两点之间最大距离时加上 dfs(v); } } } void dfs2(int u) { for(int j = 1; j <= N; ++j) for(int i = 1; i <= n; ++i) father[i][j] = father[ father[i][j-1] ][j-1]; for(int i = 0; i < G[u].size(); ++i) { int v = G[u][i].to; if(!vis[v]) { depth[u] = depth[v]+1; vis[v] = 1; //dis[v] = dis[u]+G[u][i].weight; //计算两点之间最大距离时加上 dfs(v); } } }
其实完全没必要,直接加一个update函数就完事了
void update() { for(int j = 1; j <= N; ++j) for(int i = 1; i <= n; ++i) { father[i][j] = father[ father[i][j-1] ][j-1]; } } int dfs(int u) { for(int i = 0; i < G[u].size(); ++i) { int v = G[u][i].to; if(!vis[v]) { depth[v] = depth[u]+1; vis[v] = 1; dis[v] = dis[u]+G[u][i].weight; dfs(v); } } }
然后就是LCA模板
int find_lca(int x, int y) { if(depth[x] < depth[y]) swap(x, y); //保证x一定大于y for(int i = N; i >= 0; --i) if(depth[ father[x][i] ] >= depth[y]) x = father[x][i]; if(x == y) return x; for(int i = N; i >= 0; --i) if(father[x][i] != father[y][i]) { x = father[x][i]; y = father[y][i]; } return father[x][0]; }
二、欧拉序+RMQ
首先说说欧拉序是什么
欧拉序就是从根结点出发,按dfs的顺序在绕回原点所经过所有点的顺序
2、欧拉序有怎么写?
(1)dfs到加进,dfs回加进,总共加入度遍。

原图来源于网络,并经过灵魂画师xhk的一发魔改。

欧拉序1为A-B-D-B-E-G-E-B-A-C-F-H-F-C-A
来源博客:https://www.cnblogs.com/stxy-ferryman/p/7741970.html
下面是欧拉序代码
先说明一下变量命名规则
#include <bits/stdc++.h> using namespace std; int n, m; struct node { int to, weight; node(int a, int b) { to = a; weight = b; } }; const int MX = 40000+10; int depth[2*MX], d[2*MX][64], f[2*MX], pos[MX], dis[MX]; //depth为深度,d(i, j)为点i到k最小的位置, //pos为每个点第一次出现的位置,dis为每个点到根节点的位置,f为欧拉序 vector<node> G[MX]; int vis[MX]; int cnt;
dfs求欧拉序,深度,以及到根节点的距离(假设根为1)
int dfs(int cur, int deep) //dfs树 { cnt++; //从1开始 f[cnt] = cur; pos[cur] = cnt; depth[cnt] = deep; for(int i = 0; i < G[cur].size(); ++i) { node v = G[cur][i]; if(!vis[v.to]) { dis[v.to] = dis[cur]+v.weight; vis[v.to] = 1; dfs(v.to, deep+1); cnt++; f[cnt] = cur; depth[cur] = deep; //欧拉序记录dfs回来后的位置 } } }
然后直接上LRMQ模板,找每段depth最小的点
void RMQ_init() { for(int i = 1; i <= 2*n-1; ++i) d[i][0] = i; //初始化祖先为自己 for(int j = 1; (1<<j) <= 2*n-1; ++j) for(int i = 1; i+(1<<j)-1 <= 2*n-1; ++i) { if(depth[ d[i][j-1] ] < depth[ d[i+(1<<(j-1))][j-1] ]) d[i][j] = d[i][j-1]; else d[i][j] = d[i+(1<<(j-1))][j-1]; } } int RMQ(int le, int ri) { le = pos[le]; ri = pos[ri]; if(le > ri) swap(le, ri); //为了求出len = ri-le+1 int k = 0; while(1<<(1+k) <= ri-le+1) k++; if(depth[ d[le][k] ] < depth[ d[ri-(1<<k)+1][k] ]) return f[ d[le][k] ]; else return f[ d[ri-(1<<k)+1][k] ]; //返回dp位置的点是什么 }
化繁为简 大巧不工

浙公网安备 33010602011771号