题意
传送门 POJ 1986
题解
LCA + RMQ + BIT
将树转为从根 D F S DFS DFS 标号后得到的序列,使用基于 R M Q RMQ RMQ 的算法求 L C A LCA LCA,用 B I T BIT BIT 维护边权和。
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
#define inf 0x3f3f3f3f
#define maxn 40010
#define maxm (1 << 18)
struct edge
{
int v, w;
edge() {}
edge(int v, int w) : v(v), w(w) {}
};
int N, M, K, root, id[maxn], vs[maxn * 2], dep[maxn * 2];
vector<edge> G[maxn];
int bit_n, bit[maxm], st_n, st[maxm];
void add(int i, int x)
{
while (i <= bit_n)
{
bit[i] += x;
i += i & -i;
}
}
int sum(int i)
{
int s = 0;
while (i > 0)
{
s += bit[i];
i -= i & -i;
}
return s;
}
void rmq_init(int k, int l, int r)
{
if (r - l == 1)
{
st[k] = l;
}
else
{
int chl = (k << 1) + 1, chr = (k << 1) + 2, m = (l + r) >> 1;
rmq_init(chl, l, m);
rmq_init(chr, m, r);
st[k] = dep[st[chl]] > dep[st[chr]] ? st[chr] : st[chl];
}
}
int query(int a, int b, int k, int l, int r)
{
if (r <= a || b <= l)
{
return st_n;
}
if (a <= l && r <= b)
{
return st[k];
}
int chl = (k << 1) + 1, chr = (k << 1) + 2, m = (l + r) >> 1;
int i = query(a, b, chl, l, m), j = query(a, b, chr, m, r);
return dep[i] > dep[j] ? j : i;
}
void dfs(int v, int p, int d, int &k)
{
id[v] = k, vs[k] = v, dep[k++] = d;
for (int i = 0; i < G[v].size(); i++)
{
edge &e = G[v][i];
if (e.v != p)
{
add(k, e.w);
dfs(e.v, v, d + 1, k);
vs[k] = v, dep[k++] = d;
add(k, -e.w);
}
}
}
void init(int V)
{
bit_n = (V - 1) * 2, st_n = bit_n + 1;
int k = 0;
dfs(root, -1, 0, k);
dep[st_n] = inf;
rmq_init(0, 0, st_n);
}
int lca(int u, int v)
{
return vs[query(min(id[u], id[v]), max(id[u], id[v]) + 1, 0, 0, st_n)];
}
int main()
{
scanf("%d%d", &N, &M);
for (int i = 0; i < M; i++)
{
int u, v, w;
char d;
scanf("%d%d%d %c", &u, &v, &w, &d);
--u, --v;
G[u].push_back(edge(v, w));
G[v].push_back(edge(u, w));
}
root = 0;
init(N);
scanf("%d", &K);
for (int i = 0; i < K; i++)
{
int u, v;
scanf("%d%d", &u, &v);
--u, --v;
int p = lca(u, v);
printf("%d\n", sum(id[u]) + sum(id[v]) - 2 * sum(id[p]));
}
return 0;
}
LCA + 倍增
预处理出每个顶点向根节点走 2 k 2^k 2k 步所到的节点与路径上的边权和,便可以 O ( l o g N ) O(logN) O(logN) 计算 L C A LCA LCA 的同时求出路径和。
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
#define maxn 40010
#define maxl 17
struct edge
{
int v, w;
edge() {}
edge(int v, int w) : v(v), w(w) {}
};
int N, M, K, root, dep[maxn * 2], par[maxl][maxn], dis[maxl][maxn];
vector<edge> G[maxn];
void dfs(int v, int p, int d, int c)
{
par[0][v] = p, dis[0][v] = c, dep[v] = d;
for (int i = 0; i < G[v].size(); i++)
{
edge &e = G[v][i];
if (e.v != p)
{
dfs(e.v, v, d + 1, e.w);
}
}
}
void init(int V)
{
dfs(root, -1, 0, -1);
for (int k = 0; k + 1 < maxl; k++)
{
for (int v = 0; v < V; v++)
{
par[k + 1][v] = par[k][v] == -1 ? -1 : par[k][par[k][v]];
dis[k + 1][v] = dis[k][v] == -1 ? -1 : dis[k][par[k][v]] + dis[k][v];
}
}
}
int lca(int u, int v)
{
if (dep[u] > dep[v])
{
swap(u, v);
}
int d = 0;
for (int k = 0; k < maxl; k++)
{
if ((dep[v] - dep[u]) >> k & 1)
{
d += dis[k][v], v = par[k][v];
}
}
if (u == v)
{
return d;
}
for (int k = maxl - 1; k >= 0; k--)
{
if (par[k][u] != par[k][v])
{
d += dis[k][u] + dis[k][v], u = par[k][u], v = par[k][v];
}
}
return d + dis[0][u] + dis[0][v];
}
int main()
{
scanf("%d%d", &N, &M);
for (int i = 0; i < M; i++)
{
int u, v, w;
char d;
scanf("%d%d%d %c", &u, &v, &w, &d);
--u, --v;
G[u].push_back(edge(v, w));
G[v].push_back(edge(u, w));
}
root = 0;
init(N);
scanf("%d", &K);
for (int i = 0; i < K; i++)
{
int u, v;
scanf("%d%d", &u, &v);
--u, --v;
printf("%d\n", lca(u, v));
}
return 0;
}