题目链接
题目大意
对于一棵树,求出一个点对于给定的三个点(以下简称 $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;
}
另外不要忘了双倍经验。
标签:int,题解,P10952,l1,read,l2,l3,聚会,LCA From: https://www.cnblogs.com/hirasawayuii/p/18631402