6580: 聚会 倍增LCA
描述
Y 岛风景美丽宜人,气候温和,物产丰富。Y 岛上有 N 个城市,有 N−1 条城市间的道路连接着它们。每一条道路都连接某两个城市。幸运的是,小可可通过这些道路可以走遍 Y 岛的所有城市。神奇的是,乘车经过每条道路所需要的费用都是一样的。
小可可,小卡卡和小 YY 经常想聚会,每次聚会,他们都会选择一个城市,使得三个人到达这个城市的总费用最小。
由于他们计划中还会有很多次聚会,每次都选择一个地点是很烦人的事情,所以他们决定把这件事情交给你来完成。他们会提供给你地图以及若干次聚会前他们所处的位置,希望你为他们的每一次聚会选择一个合适的地点。
输入
第一行两个正整数,N 和 M。分别表示城市个数和聚会次数;
后面有 N−1 行,每行用两个正整数 A 和 B 表示编号为 A 和编号为 B 的城市之间有一条路。城市的编号是从 1 到 N 的;
再后面有 M 行,每行用三个正整数表示一次聚会的情况:小可可所在的城市编号,小卡卡所在的城市编号以及小 YY 所在的城市编号。
40% 的数据中,1≤N,M≤2×103 ;
100% 的数据中,1≤N,M≤5×105。
输出
一共有 M 行,每行两个数 P 和 C,用一个空格隔开。表示第 i 次聚会的地点选择在编号为 P 的城市,总共的费用是经过 C 条道路所花费的费用。
样例输入
6 4
1 2
2 3
2 4
4 5
5 6
4 5 6
6 3 1
2 4 4
6 6 6
样例输出
5 2
2 5
4 1
6 0
原理:三个结点的最近公共祖先里必有两个是相同的,而最近的点就是第三个祖先,所以求出第三个祖先后再计算三个节点到这个祖先的距离就是答案
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 5e5+10,inf = 0x3f3f3f3f; struct node{ int to,nex; }; node e[N * 2]; int head[N * 2],cnt; int f[N][21],dis[N],dep[N]; int n,m,x,y,z; void add(int x,int y) { e[++cnt].to = y; e[cnt].nex = head[x]; head[x] = cnt; } void dfs(int x,int fa) { dep[x] = dep[fa] + 1; for(int i = 1; i <= 20; i++) f[x][i] = f[f[x][i - 1]][i - 1]; for(int i = head[x]; i; i = e[i].nex) { int y = e[i].to; if(y == fa)continue; f[y][0] = x; dfs(y,x); } } int lca(int x,int y) { if(dep[x] < dep[y])swap(x,y); for(int i = 20; i >= 0; i--) { if(dep[f[x][i]] >= dep[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]; } int main() { cin >> n >> m; for(int i = 1; i < n; i++) { scanf("%d %d",&x,&y); add(x,y);add(y,x); } dfs(1,0); while(m--) { scanf("%d %d %d",&x,&y,&z); int t1 = lca(x,y),t2 = lca(x,z),t3 = lca(y,z); int pos; if(t1 == t2) pos = t3; else if(t1 == t3) pos = t2; else pos = t1; int ans = dep[x] + dep[y] + dep[z] - dep[t1] - dep[t2] - dep[t3]; printf("%d %d\n",pos,ans); } return 0; }