题目链接
题目大意
对于一棵树,求出一个点对于给定的三个点(以下简称 $x$,$y$,$z$ 且可以重复)距离最短。
题解
对于点的距离,不难想到 LCA 处理。而对于本题,则有两种情况。
第一问
-
三点中有一为另外两个点的祖先时,所求目标点(以下简称 $v$ )的深度(简称 $d_v$ )一定在三点深度之间。
-
三点共同 LCA 为另一点时,$v$ 即为三点的 LCA 。
这两种情况包含了所有形式,所以我们可以通过求出 $\operatorname {LCA(x,y)}$,$\operatorname {LCA(x,z)}$,$\operatorname {LCA(y,z)}$ 后选取最深的点,即所求的 $v$。对于此结论,第二种情况自然不用多说,而第一种情况中显然 $d_x \le d_v \le d_y$($d_x \le d_y \le d_z$),最深的点可以使 $y$ 与 $z$ 到 $v$ 的距离最短,即 $y$ 与 $z$ 不会走相同的边。
第二问
设点 $x$ 到根节点的距离为 $dis_x$,由树的性质得答案为 $dis_x+dis_y+dis_z-dis_{\operatorname{LCA(x,y)}}-dis_{\operatorname{LCA(x,z)}}-dis_{\operatorname{LCA(y,z)}}$。
代码
#include <bits/stdc++.h>
using namespace std;
#define mst(x, y) memset(x, y, sizeof(x))
#define pp pair<int, int>
#define fi first
#define se second
#define mp(x, y) make_pair(x, y)int read(){int x = 0, f = 1;char c = getchar();while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();}while(c >= '0' && c <= '9'){x = 10*x+c-'0';c = getchar();}return f*x;}
void write(int x){if(x < 0){putchar('-');x = -x;}if(x > 9) write(x/10);putchar(x%10 | 0x30);return;}
const int N = 1000005, inf = 0x3f3f3f3f;int n, q, hd[N], ver[N], nxt[N], idx, d[N], f[N][25], ans, mk[N];void add(int x, int y){nxt[++idx] = hd[x];ver[idx] = y;hd[x] = idx;
}
void bfs(){queue <int> q;q.push(1);mk[1] = 1, d[1] = 1;while(q.size()){int t = q.front();q.pop();for(int i = hd[t];i;i = nxt[i]){int y = ver[i];if(mk[y]) continue;mk[y] = 1;d[y] = d[t]+1, f[y][0] = t;for(int j = 1;j <= 20;j++){f[y][j] = f[f[y][j-1]][j-1];}q.push(y);}}
}
int lca(int x, int y){if(d[x] < d[y]) swap(x, y);for(int i = 20;i >= 0;i--){if(d[f[x][i]] >= d[y]) x = f[x][i];}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];
}
void init(){n = read(), q = read();for(int i = 1;i < n;i++){int x = read(),y = read();add(x, y);add(y, x);}bfs();
}
void solve(){while(q--){int x = read(), y = read(), z = read();int l1 = lca(x, y), l2 = lca(y, z), l3 = lca(x, z);ans = d[x]+d[y]+d[z]-d[l1]-d[l2]-d[l3];if(d[l1] >= d[l2] && d[l1] >= d[l3]){write(l1);putchar(' ');write(ans);puts("");}else if(d[l2] >= d[l1] && d[l2] >= d[l3]){write(l2);putchar(' ');write(ans);puts("");}else if(d[l3] >= d[l2] && d[l3] >= d[l1]){write(l3);putchar(' ');write(ans);puts("");}}
}int main(){init();solve();return 0;
}
另外不要忘了双倍经验。