这题很明显就是可以先两个lca,然后得到一个祖先p后再和另外一个,点进行lca得到三个点的最近公共祖先p2,此时三个点之间的距离其实已经求出来了,而这两个最近公共祖先p和p2里面,我们自然是选择深度较高的那个作为答案。
然后一个三个数字一共三种这样的情况,枚举一下,不断更新答案即可。
但是看了别人的做法发现有一种更加简单的方法,就是直接两两lca得到三个最近公共祖先p1,p2,p3,如果有两个祖先的标号是相同的,那么我们肯定是选择另外一个当作答案,其实说白了就是上面的方法,尽量用深度较高的点。得到这个点之后将这个祖先p和三个点都求一次lca,三个距离加起来既是答案。这种操作简洁得多。
两份代码如下:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
//#pragma comment(linker, "/STACK:102400000,102400000") //不需要申请系统栈
inline int read() {
char ch = getchar();
int f = 1, x = 0;
while(!(ch >= '0' && ch <= '9')) {
if(ch == '-')
f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9') {
x = x * 10 + (ch - '0');
ch = getchar();
}
return x * f;
}
const int maxn = 5e5 + 5;
const int M = 25;
int dp[2 * maxn][M]; //这个数组记得开到2*maxn,因为遍历后序列长度为2*n-1
bool vis[maxn];
struct edge
{
int u, v, w, next;
}e[2 * maxn];
int tot, head[maxn];
inline void add(int u ,int v ,int w ,int &k) {
e[k].u = u; e[k].v = v; e[k].w = w;
e[k].next = head[u]; head[u] = k++;
u = u ^ v; v = u ^ v; u = u ^ v;
e[k].u = u; e[k].v = v; e[k].w = w;
e[k].next = head[u]; head[u] = k++;
}
int ver[2 * maxn], R[2 * maxn], first[maxn], dir[maxn];
//ver:节点编号 R:深度 first:点编号位置 dir:距离
void dfs(int u ,int dep) {
vis[u] = true; ver[++tot] = u; first[u] = tot; R[tot] = dep;
for(int k = head[u]; ~k; k = e[k].next)
if( !vis[e[k].v] )
{
int v = e[k].v , w = e[k].w;
dir[v] = dir[u] + w;
dfs(v, dep + 1);
ver[++tot] = u; R[tot] = dep;
}
}
void ST(int n) {
for(int i = 1; i <= n; i++)
dp[i][0] = i;
for(int j = 1; (1<<j) <= n; j++) {
for(int i = 1; i + (1<<j) - 1 <= n; i++) {
int a = dp[i][j-1] , b = dp[i + (1<<(j - 1))][j - 1];
dp[i][j] = R[a] < R[b] ? a : b;
}
}
}
//中间部分是交叉的。
int RMQ(int l,int r) {
int k = 0;
while((1<<(k + 1)) <= r - l + 1)
k++;
int a = dp[l][k], b = dp[r - (1<<k) + 1][k]; //保存的是编号
return R[a] < R[b] ? a : b;
}
int LCA(int u ,int v) {
int x = first[u] , y = first[v];
if(x > y) swap(x,y);
int res = RMQ(x,y);
return ver[res];
}
int count_dis(int x, int y) {
int lca = LCA(x, y);
return dir[x] + dir[y] - 2 * dir[lca];
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
//freopen("Out.txt","w",stdout);
#endif
int n, q, num = 0;
scanf("%d%d", &n, &q);
memset(head, -1, sizeof(head));
memset(vis, false, sizeof(vis));
for(int i = 1; i < n; i++) {
int u = read(), v = read();
add(u, v, 1, num);
}
tot = 0; dir[1] = 0;
dfs(1,1);
ST(2 * n - 1);
while(q--) {
int x = read(), y = read(), z = read();
int lca1, lca2, tmp, ans = 0x3f3f3f3f, key = -1;
lca1 = LCA(x, y);//lca(x,y) and z
tmp = dir[x] + dir[y] - 2 * dir[lca1];
lca2 = LCA(lca1, z);
tmp += dir[lca1] + dir[z] - 2 * dir[lca2];
if(tmp < ans) {
ans = tmp;
if(dir[lca1] < dir[lca2])
key = lca2;
else
key = lca1;
}
lca1 = LCA(y, z);//lca(y,z) and x
tmp = dir[y] + dir[z] - 2 * dir[lca1];
lca2 = LCA(lca1, x);
tmp += dir[lca1] + dir[x] - 2 * dir[lca2];
if(tmp < ans) {
ans = tmp;
if(dir[lca1] < dir[lca2])
key = lca2;
else
key = lca1;
}
lca1 = LCA(x, z);//lca(x,z) and y
tmp = dir[x] + dir[z] - 2 * dir[lca1];
lca2 = LCA(lca1, y);
tmp += dir[lca1] + dir[y] - 2 * dir[lca2];
if(tmp < ans) {
ans = tmp;
if(dir[lca1] < dir[lca2])
key = lca2;
else
key = lca1;
}
printf("%d %d\n", key, ans);
}
return 0;
}
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
//#pragma comment(linker, "/STACK:102400000,102400000") //不需要申请系统栈
inline int read() {
char ch = getchar();
int f = 1, x = 0;
while(!(ch >= '0' && ch <= '9')) {
if(ch == '-')
f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9') {
x = x * 10 + (ch - '0');
ch = getchar();
}
return x * f;
}
const int maxn = 5e5 + 5;
const int M = 25;
int dp[2 * maxn][M]; //这个数组记得开到2*maxn,因为遍历后序列长度为2*n-1
bool vis[maxn];
struct edge
{
int u, v, w, next;
}e[2 * maxn];
int tot, head[maxn];
inline void add(int u ,int v ,int w ,int &k) {
e[k].u = u; e[k].v = v; e[k].w = w;
e[k].next = head[u]; head[u] = k++;
u = u ^ v; v = u ^ v; u = u ^ v;
e[k].u = u; e[k].v = v; e[k].w = w;
e[k].next = head[u]; head[u] = k++;
}
int ver[2 * maxn], R[2 * maxn], first[maxn], dir[maxn];
//ver:节点编号 R:深度 first:点编号位置 dir:距离
void dfs(int u ,int dep) {
vis[u] = true; ver[++tot] = u; first[u] = tot; R[tot] = dep;
for(int k = head[u]; ~k; k = e[k].next)
if( !vis[e[k].v] )
{
int v = e[k].v , w = e[k].w;
dir[v] = dir[u] + w;
dfs(v, dep + 1);
ver[++tot] = u; R[tot] = dep;
}
}
void ST(int n) {
for(int i = 1; i <= n; i++)
dp[i][0] = i;
for(int j = 1; (1<<j) <= n; j++) {
for(int i = 1; i + (1<<j) - 1 <= n; i++) {
int a = dp[i][j-1] , b = dp[i + (1<<(j - 1))][j - 1];
dp[i][j] = R[a] < R[b] ? a : b;
}
}
}
//中间部分是交叉的。
int RMQ(int l,int r) {
int k = 0;
while((1<<(k + 1)) <= r - l + 1)
k++;
int a = dp[l][k], b = dp[r - (1<<k) + 1][k]; //保存的是编号
return R[a] < R[b] ? a : b;
}
int LCA(int u ,int v) {
int x = first[u] , y = first[v];
if(x > y) swap(x,y);
int res = RMQ(x,y);
return ver[res];
}
int count_dis(int x, int y) {
int lca = LCA(x, y);
return dir[x] + dir[y] - 2 * dir[lca];
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
//freopen("Out.txt","w",stdout);
#endif
int n, q, num = 0;
scanf("%d%d", &n, &q);
memset(head, -1, sizeof(head));
memset(vis, false, sizeof(vis));
for(int i = 1; i < n; i++) {
int u = read(), v = read();
add(u, v, 1, num);
}
tot = 0; dir[1] = 0;
dfs(1,1);
ST(2 * n - 1);
while(q--) {
int x = read(), y = read(), z = read();
int lca1 = LCA(x, y), lca2 = LCA(y, z), lca3 = LCA(x, z);
int key = -1, ans = 0;
if(lca1 == lca2) {
key = lca3;
} else if(lca2 == lca3) {
key = lca1;
} else if(lca1 == lca3) {
key = lca2;
}
ans += count_dis(key, x);
ans += count_dis(key, y);
ans += count_dis(key, z);
printf("%d %d\n", key, ans);
}
return 0;
}