- 一堆废话
- 代码
首先暴力做法:
分别从两个点开始一层一层地向上跳,直到跳到一样的点,这个点就是他俩的LCA了。
这个做法实在太暴力了,不可取,不可取. . .
有一个不那么暴力的做法——倍增法。
预处理复杂度:O(nlogn)
询问复杂度:O(Qlogn) Q : 询问组数
其(da)实(gai)就是一跳跳好几层。
设 fa[i][j] 记录从 i 点向上跳 2j 层所到达的点,fa数组是预处理得出的(还有每个点的深度deep也是),后面会说。
_____________先看LCA的求法____________________________________________
对于两个点的位置有两种情况:
① 位于同一层:
这时只要两个点一起跳,直到跳到相同的点即可。
for (int i = 20; i >= 0; i--) {
if (fa[x][i] != fa[y][i]) {
x = fa[x][i];
y = fa[y][i];
}
}
② 不同层:
将深度更深的点向上跳到与另一个点同一层,即转化为①。
if (node[x].deep < node[y].deep) {
int t = x; // 保证 x 是更深的那个点
x = y;
y = t;
}
for (int i = 0; i <= 20; i++) {
if ((node[x].deep - node[y].deep)>> i & 1) x = fa[x][i];
}
if (x == y) return x; // 特判:两个点在同一条链上的情况(即:它们其中一个点就是它俩的LCA)
______________预处理__________________________________________________
一个 dfs 搞定(从根结点开始DFS)。
void dfs(int s) {
for (int k = 1; k <= 20; k++)
fa[s][k] = fa[fa[s][k - 1]][k - 1];
for (Edge *e = node[s].last; e; e = e->next) {
if (!e->to->deep) {
e->to->deep = node[s].deep + 1;
fa[e->to->ma][0] = node[s].ma;
dfs(e->to->ma);
}
}
}
完整代码:
#include <cstdio>
const int MAXN = 30007;
int n, m, fa[MAXN][30], a[MAXN], ans;
/**********************快********************************/
int read() {
int x = 0;
char ch = getchar();
while (ch < '0' || ch > '9') ch = getchar();
while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
return x;
}
/************************读******************************/
struct Edge;
struct Node {
Edge *last;
int deep, ma;
bool vis;
} node[MAXN];
struct Edge {
Node *from, *to;
Edge *next;
Edge (Node *from, Node *to) : from(from), to(to), next(from->last) {}
};
void addE(int x, int y) {
node[x].last = new Edge(node + x, node + y);
node[y].last = new Edge(node + y, node + x);
}
void dfs(int s) {
for (int k = 1; k <= 20; k++) // 注意!!!!
fa[s][k] = fa[fa[s][k - 1]][k - 1];
for (Edge *e = node[s].last; e; e = e->next) {
if (!e->to->deep) {
e->to->deep = node[s].deep + 1;
fa[e->to->ma][0] = node[s].ma;
dfs(e->to->ma);
}
}
}
int lca (int x, int y) {
if (node[x].deep < node[y].deep) {
int t = x;
x = y;
y = t;
}
for (int i = 0; i <= 20; i++) {
if ((node[x].deep - node[y].deep)>> i & 1) x = fa[x][i];
}
if (x == y) return x;
for (int i = 20; i >= 0; i--) {
if (fa[x][i] != fa[y][i]) {
x = fa[x][i];
y = fa[y][i];
}
}
return fa[x][0];
}
int main() {
n = read();
for (int i = 1; i <= n; i++) node[i].ma = i;
for (int i = 1, b, c; i < n; i++) {
c = read();
b = read();
addE(c, b);
}
fa[1][0] = 1;
node[1].deep = 1;
dfs(1);
m = read();
for (int i = 1; i <= m; i++) {
a[i] = read();
}
for (int i = 2; i <= m; i++) {
ans += node[a[i]].deep + node[a[i - 1]].deep - 2 * node[lca(a[i - 1], a[i])].deep;
}
printf("%d\n", ans);
return 0;
}
例题:
1、商务旅行 √
2、小机房的树 √
3、水果姐逛水果街 ×