1787: [Ahoi2008]Meet 紧急集合
Time Limit: 20 Sec Memory Limit: 162 MB
Submit: 999999999 Solved: 999999999
Description
Input
Output
Sample Input
6 4
1 2
2 3
2 4
4 5
5 6
4 5 6
6 3 1
2 4 4
6 6 6
Sample Output
5 2
2 5
4 1
6 0
HINT
三个点分别组合,算出LCA,其中必有两个是一样的;
然后既然得知了LCA是哪两个点相同,可以唯一确定更深的另一个LCA,两个点的移动必定不如一个点的移动,由此得解
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 500010;
const int M = N + N;
const int P = 16;
int n, m;
int head[N], dest[M], last[M], dast[M], etot;
int dep[N], anc[N][P+1];
void adde( int u, int v ) {
etot++;
dest[etot] = v;
last[etot] = head[u];
head[u] = etot;
}
void dfs( int u, int f ) {
anc[u][0] = f;
for( int p = 1; p <= P; p++ )
anc[u][p] = anc[anc[u][p-1]][p-1];
for( int t = head[u]; t; t = last[t] ) {
int v = dest[t];
if( v == f ) continue;
dep[v] = dep[u] + 1;
dfs( v, u );
}
}
int lca( int u, int v ) {
if( dep[u] < dep[v] ) swap(u,v);
int t = dep[u] - dep[v];
for( int p = 0; t; t>>=1,p++ )
if( t & 1 ) u = anc[u][p];
if( u == v ) return u;
for( int p = P; p >= 0; p-- )
if( anc[u][p] != anc[v][p] )
u = anc[u][p], v = anc[v][p];
return anc[u][0];
}
int main() {
scanf( "%d%d", &n, &m );
int u, v, w;
for( int i = 1; i < n; i++ ) {
scanf( "%d%d", &u, &v );
adde( u, v );
adde( v, u );
}
dep[1] = 1;
dfs( 1, 1 );
while(m--){
scanf( "%d%d%d", &u, &v, &w );
int s=dep[u]+dep[v]+dep[w];
int t=lca(u,v);
int q=lca(v,w);
int r=lca(u,w);
if( t == q ){
w = lca( r, v );
printf( "%d %d\n", r, s-dep[w]*2-dep[r]);
}else if( t == r ){
w=lca( q, u );
printf("%d %d\n",q,s-dep[w]*2-dep[q]);
}else if( r == q ){
w=lca( t, w );
printf("%d %d\n",t,s-dep[w]*2-dep[t]);
}
}
return 0;
}

本文提供了一道算法题目的解析——Ahoi2008紧急集合,介绍了如何通过构建树状结构来求解点对间的最近公共祖先(LCA)问题,并给出完整的C++代码实现。

169

被折叠的 条评论
为什么被折叠?



