题意:
多次询问树上两点最短 距离。
TLE做法:对每次询问跑一遍最短路,用堆优化迪杰斯特拉的话复杂度大概为\(O(q*(n+m)logm)\)。
\(100\)分做法:
用一个\(dis\)数组记录根节点\(root\)到每一个节点的距离,那么树上两点\(u,v\)的距离就是\(root\)到\(u\)的距离加上\(root\)到\(v\)的距离减去两倍的\(root\)到\(lca(u,v)\)的距离(可以画下图感性理解...),即
\(ans=dis[u]+dis[v]-2*dis[lca(u,v)]\)
我用的是树上倍增求\(lca\)。
最近公共祖先(\(lca\))是指在一个树或者有向无环图中同时拥有\(v\)和\(w\)作为后代的最深的节点,最近公共祖先是两个节点所有公共祖先中离根节点最远的。
其实就是\(lca\)模板题,看几篇博客就会了。
具体求法可以参考一下大佬的博客浅谈最近公共祖先
最近公共祖先
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define endl '\n'
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> pii;
const int maxn = 75000 + 5;
const int maxm = 100 + 5;
const int inf = 0x3f3f3f3f;
const LL mod = 1e9 + 7;//19260817
const double pi = acos(-1.0);
int n, q, cnt, x, y, deep[maxn], pre[maxn][20], head[maxn], dis[maxn];
struct node {
int v, w, next;
} edge[maxn << 1];
void addedge(int u, int v, int w) {
edge[++cnt].v = v;
edge[cnt].w = w;
edge[cnt].next = head[u];
head[u] = cnt;
}
void dfs(int x, int f) {
pre[x][0] = f;
for(int i = head[x]; i; i = edge[i].next) {
if(edge[i].v != f) {
deep[edge[i].v] = deep[x] + 1;
dis[edge[i].v] = dis[x] + edge[i].w;
dfs(edge[i].v, x);
}
}
}
void solve() {
for(int j = 1; j <= 19; j++)
for(int i = 1; i <= n; i++)
pre[i][j] = pre[pre[i][j - 1]][j - 1];
}
int lca(int u, int v) {
if(deep[u] < deep[v]) swap(u, v);
int dc = deep[u] - deep[v];
for(int i = 0; i <= 19; i++) {
if((1 << i) & dc) u = pre[u][i];
}
if(u == v) return u;
for(int i = 19; ~i; i--) {
if(pre[u][i] != pre[v][i]) {
u = pre[u][i];
v = pre[v][i];
}
}
return pre[u][0];
}
int main() {
scanf ("%d", &n);
for (int i = 1, u, v, w; i <= n - 1; i++) {
scanf ("%d %d %d", &u, &v, &w);
++u, ++v;
addedge(u, v, w), addedge(v, u, w);
}
dfs(1, -1);
solve();
scanf ("%d", &q);
while (q--) {
scanf ("%d %d", &x, &y);
++x, ++y;
printf ("%d\n", dis[x] + dis[y] - 2 * dis[lca(x, y)]);
}
return 0;
}
原文地址:https://www.cnblogs.com/ChaseNo1/p/11366608.html
时间: 2024-10-25 14:10:39