LCA(树上最近公共祖先)
LCA即是树上最近公共祖先算法,这里给出模板,主要以练习题集为主。
1、模板题
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
int head[N], cnt;
struct node
{
int to, nex;
} e[N << 1];
void add_edge(int u, int v)
{
e[++cnt].to = v;
e[cnt].nex = head[u];
head[u] = cnt;
}
int dep[N], f[N][30], clg[N];
void dfs(int u, int fa)
{
f[u][0] = fa;
dep[u] = dep[fa] + 1;
for(int i = 1; i <= clg[dep[u]]; ++i)
{
f[u][i] = f[f[u][i - 1]][i - 1];
}
for(int i = head[u]; i; i = e[i].nex)
{
if(e[i].to != fa) dfs(e[i].to, u);
}
}
int lca(int x, int y)
{
if(dep[x] < dep[y]) swap(x, y);
while(dep[x] > dep[y]) x = f[x][clg[dep[x] - dep[y]] - 1];
if(x == y) return x;
for(int i = clg[dep[x]] - 1; i >= 0; --i)
{
if(f[x][i] != f[y][i])
{
x = f[x][i];
y = f[y][i];
}
}
return f[x][0];
}
int n, m, s;
signed main()
{
ios::sync_with_stdio(false), cin.tie(0);
cin >> n >> m >> s;
int x, y;
for(int i = 1; i <= n - 1; ++i)
{
cin >> x >> y;
add_edge(x, y);
add_edge(y, x);
}
for(int i = 1; i <= n; ++i) clg[i] = clg[i - 1] + (1 << clg[i - 1] == i);
dfs(s, 0);
while(m--)
{
cin >> x >> y;
cout << lca(x, y) << "\n";
}
return 0;
}
2、树上任意两点距离
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 4e4 + 100;
int cnt, head[N];
struct node
{
int to, nex, w;
} e[N << 1];
void add_edge(int u, int v, int w)
{
e[++cnt].to = v;
e[cnt].w = w;
e[cnt].nex = head[u];
head[u] = cnt;
}
int dep[N], clg[N], f[N][30], dis[N];
void dfs(int u, int fa)
{
f[u][0] = fa;
dep[u] = dep[fa] + 1;
for(int i = 1; i <= clg[dep[u]]; ++i)
{
f[u][i] = f[f[u][i - 1]][i - 1];
}
for(int i = head[u]; i; i = e[i].nex)
{
int v = e[i].to;
if(v != fa)
{
dis[v] = dis[u] + e[i].w;
dfs(v, u);
}
}
}
int lca(int x, int y)
{
if(dep[x] < dep[y]) swap(x, y);
while(dep[x] > dep[y]) x = f[x][clg[dep[x] - dep[y]] - 1];
if(x == y) return x;
for(int i = clg[dep[x]] - 1; i >= 0; --i)
{
if(f[x][i] != f[y][i])
{
x = f[x][i];
y = f[y][i];
}
}
return f[x][0];
}
//公式
int cal_dis(int u, int v)
{
return dis[u] + dis[v] - 2 * dis[lca(u, v)];
}
int n, q, u, v, w;
signed main()
{
ios::sync_with_stdio(false), cin.tie(0);
for(int i = 1; i < N; ++i) clg[i] = clg[i - 1] + (1 << clg[i - 1] == i);
int t;
cin >> t;
while(t--)
{
cnt = 0;
memset(head, -1, sizeof head);
dis[1] = dep[1] = 0;
cin >> n >> q;
for(int i = 1; i <= n - 1; ++i)
{
cin >> u >> v >> w;
add_edge(u, v, w);
add_edge(v, u, w);
}
dfs(1, 0);
while(q-- && cin >> u >> v) cout << cal_dis(u, v) << "\n";
}
return 0;
}
3、小A的最短路
Solution
传送门
因为有两个点之间通过免费,可以使用s-t作为中转,计算所有情况最小即可。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + 10;
int cnt, head[N];
struct node
{
int to, nex;
} e[N << 1];
void add_edge(int u, int v)
{
e[++cnt].to = v;
e[cnt].nex = head[u];
head[u] = cnt;
}
int dep[N], clg[N], f[N][30];
void dfs(int u, int fa)
{
f[u][0] = fa;
dep[u] = dep[fa] + 1;
for(int i = 1; i <= clg[dep[u]]; ++i)
{
f[u][i] = f[f[u][i - 1]][i - 1];
}
for(int i = head[u]; i; i = e[i].nex)
{
if(e[i].to != fa) dfs(e[i].to, u);
}
}
int lca(int x, int y)
{
if(dep[x] < dep[y]) swap(x, y);
while(dep[x] > dep[y]) x = f[x][clg[dep[x] - dep[y]] - 1];
if(x == y) return x;
for(int i = clg[dep[x]] - 1; i >= 0; --i)
{
if(f[x][i] != f[y][i])
{
x = f[x][i];
y = f[y][i];
}
}
return f[x][0];
}
int cal_dis(int x, int y)
{
//cout << x << " " << y << " " << lca(x, y) << "lca\n";
return dep[x] + dep[y] - 2 * dep[lca(x, y)];
}
int n, m, s, t;
signed main()
{
ios::sync_with_stdio(false), cin.tie(0);
cnt = 0;
memset(head, -1, sizeof head);
cin >> n;
for(int i = 1; i <= n; ++i) clg[i] = clg[i - 1] + (1 << clg[i - 1] == i);
int x, y;
for(int i = 1; i <= n - 1; ++i)
{
cin >> x >> y;
add_edge(x, y);
add_edge(y, x);
}
dfs(1, 0);
cin >> s >> t;
cin >> m;
while(m-- && cin >> x >> y)
{
int ans = cal_dis(x, y);
ans = min({ans, cal_dis(x, s) + cal_dis(t, y), cal_dis(x, t) + cal_dis(s, y)});
cout << ans << "\n";
}
return 0;
}

浙公网安备 33010602011771号