古人云:倍增是做不好LCA的,只有tarjan才行…
原题链接
浅浅画了个图
tarjan思维就是,假设我现在在用bfs搜索,我搜到某一个点,假设这个点是某已搜过点(a)和某未搜过点(b)的 最小公共祖先,那么此时我们只需要预先处理一下每一个点(dist数组)和根节点之间的距离,即可计算两点之间的距离的时候,此时需要计算一个:
len ( a, b ) = dist [ a ] + dist [ b ] - 2 * dist[ LCA ];
y总在模板里存数据用的是vector,这个方法我确实是没学过,确实是学习了
====================================================
题目:
给出 n 个点的一棵树,多次询问两点之间的最短距离。
注意:
边是无向的。
所有节点的编号是 1,2,…,n。
输入格式
第一行为两个整数 n 和 m。n 表示点数,m 表示询问次数;
下来 n−1 行,每行三个整数 x,y,k,表示点 x 和点 y 之间存在一条边长度为 k;
再接下来 m 行,每行两个整数 x,y,表示询问点 x 到点 y 的最短距离。
树中结点编号从 1 到 n。
输出格式
共 m 行,对于每次询问,输出一行询问结果。
数据范围
2≤n≤104,
1≤m≤2×104,
0<k≤100,
1≤x,y≤n
输入样例1:
2 2
1 2 100
1 2
2 1
输出样例1:
100
100
输入样例2:
3 2
1 2 10
3 1 15
1 2
3 2
输出样例2:
10
25
难度:中等
时/空限制:1s / 64MB
总通过数:4461
总尝试数:9386
来源:《信息学奥赛一本通》
算法标签
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5;
LL idx, h[N * 2], e[N * 2], ne[N * 2], w[N *2];
LL n, m, depth[N * 2], f[N][16], p[N], len[N][16];
void add ( LL a, LL b , LL c ) {
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}
void bfs () {
memset( depth, 0x3f, sizeof depth );
depth[0] = 0; depth[1] = 1;
LL hh = 0, tt = 0;
p[0] = 1;
while ( hh <= tt ) {
int t = p[hh ++ ];
for ( int i = h[t]; ~i; i = ne[i] ) {
LL s = e[i];
// cout << s << " s" << endl;
if ( depth[s] > depth[t] + 1 ) {
depth[s] = depth[t] + 1;
f[s][0] = t, len[s][0] = w[i];
for ( int i = 1; i <= 15; i++ ) {
f[s][i] = f[f[s][i - 1]][i - 1];
len[s][i] = len[len[s][i - 1]][i - 1];
}
p[++tt] = s;
}
}
}
}
LL lca ( int a, int b ) {
if ( depth[b] > depth[a] ) swap( a, b );
// cout << "lca" << endl;
LL res = 0;
// cout << depth[a] << " " << depth[b] << endl;
for ( int i = 15; i >= 0; i -- ) {
if ( depth[f[a][i]] >= depth[b] ) {
res += len[a][i];
// cout << i << " " << f[a][i] << " " << depth[b] << endl;
a = f[a][i];
}
}
// cout << a << " debug " << b << endl;
if ( a == b ) return res;
for ( int i = 15; i >= 0; i -- ) {
if ( f[a][i] != f[b][i] ) {
res += ( len[a][i] + len[b][i] );
a = f[a][i],b = f[b][i];
}
}
// cout << res << endl;
res += len[a][0] + len[b][0];
// cout << res << endl;
return res;
}
int main () {
memset( h, -1, sizeof h );
scanf( "%d%d", &n, &m );
for ( int i = 1; i <= n - 1; i ++ ) {
LL a, b, c;
scanf( "%lld%lld%lld", &a, &b, &c );
add( a, b, c );
add( b, a, c );
}
// cout << "in" << endl;
bfs ();
// cout << "out" << endl;
for ( int i = 1; i <= m; i++ ) {
LL a, b;
scanf( "%lld%lld" , &a, &b );
//cout << a << " " << b << endl;
cout << lca ( a, b ) << endl;
}
}