标签:节点 algorithm add math head ret 两种方法 swa 最近公共祖先
这种算法本质上是用并查集对向上标记法的优化,是离线算法,即一次性读入所有询问,统一计算,统一输出。
时间复杂度\(O(n+m)\)
v[]
进行标记code
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 1e5+5;
struct side {
int t, d, next;
}e[N<<1];
int head[N], tot;
void add(int x, int y, int z) {
e[++tot].next = head[x];
head[x] = tot;
e[tot].t = y, e[tot].d = z;
}
int n, m, Q, d[N], f[N], v[N], ans[N];
vector<int> q[N], h[N];
int found(int x) {
return x == f[x] ? x : (f[x] = found(f[x]));
}
void Dfs(int x) {
v[x] = 1;
for (int i = head[x]; i; i = e[i].next) {
int y = e[i].t;
if (v[y]) continue;
d[y] = d[x] + e[i].d;
Dfs(y);
f[y] = x;
}
for (int i = 0; i < q[x].size(); i++) {
int y = q[x][i], id = h[x][i];
if (v[y] != 2) continue;
ans[id] = min(ans[id], d[x] + d[y] - 2*d[found(y)]);
}
v[x] = 2;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) f[i] = i;
while (m--) {
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
add(x, y, z); add(y, x, z);
}
scanf("%d", &Q);
for (int i = 1; i <= Q; i++) {
int x, y;
scanf("%d%d", &x, &y);
if (x == y) continue;
ans[i] = 1 << 30;
q[x].push_back(y), h[x].push_back(i);
q[y].push_back(x), h[y].push_back(i);
}
Dfs(1);
for (int i = 1; i <= Q; i++)
printf("%d\n", ans[i]);
return 0;
}
f[x][k]表示从x向根节点走\(2^k\)步到达的节点
d[x]表示树的深度
预处理时间复杂度为\(O(n\log n)\),每次询问时间复杂度为\(O(\log n)\)
code
int d[N], f[N][21];
void dfs(int x, int fa) {
f[x][0] = fa;
d[x] = d[fa] + 1;
for (int i = 1; i <= 20; i++)
f[x][i] = f[f[x][i-1]][i-1];
for (int i = head[x]; i; i = e[i].next)
if (e[i].t != fa) dfs(e[i].t, x);
}
int lca(int x, int y) {
if (d[x] > d[y]) swap(x, y);
y = found(y, d[y] - d[x]);
if (x == y) return x;
for (int i = 20; i >= 0; i--)
if (f[x][i] != f[y][i])
x = f[x][i], y = f[y][i];
return f[x][0];
}
标签:节点 algorithm add math head ret 两种方法 swa 最近公共祖先
原文地址:https://www.cnblogs.com/Z8875/p/13277399.html