题意
一棵树,边长1,求走过k个点花费的最小时间
思路
找到树最长的那段路,即树的直径
如果访问的点的个数小于直径上点的个数,则可以直接到,时间就等于点的个数
如果超过直径上点的个数,则需要中途离开直径访问别的店并返回直径,则访问其他店要花费两倍的时间来往返
求直径的方法:
任取一点找到距离他最远的点,作为直径的起点
从起点找到离他最远的点,则是终点,,,
证明…..略………qwq
(是小庆庆的面试题呢!
代码
#include <algorithm>
#include <bits/stdc++.h>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;
const int INF = 0x7f7f7f7f;
const int maxn = 1e5 + 10;
int n, m;
vector<int> edg[ maxn ];
int dis[ maxn ];
void dfs ( int now, int last, int len ) {
dis[ now ] = len; //记录每个点距离开始点的距离
for ( int i = 0; i < (int)edg[ now ].size (); ++i ) {
int u = edg[ now ][ i ];
if ( u != last ) {
dfs ( u, now, len + 1 );
}
}
}
//求直径
int st, ed;
int longestpath () {
st = 0, ed = 0;
//任意取一个点,找出距离他最远的点,这个点是直径的一端
dfs ( 1, 0, 0 );
for ( int i = 1; i <= n; ++i )
if ( dis[ st ] < dis[ i ] )
st = i;
//从直径的一端开始,离他最远的点是直径的另一端
dfs ( st, 0, 0 );
for ( int i = 1; i <= n; ++i )
if ( dis[ ed ] < dis[ i ] )
ed = i;
int mxlen = dis[ ed ];
return mxlen;
}
int main () {
#ifdef LOCAL
freopen ( "in", "r", stdin );
// freopen("out","w",stdout);
#endif
int t;
scanf ( "%d", &t );
while ( t-- ) {
scanf ( "%d%d", &n, &m );
//忘记清零,再往里面放入边不但出错而且超内存***
for ( int i = 0; i <= n; ++i )
edg[ i ].clear ();
for ( int i = 0, u, v; i < n - 1; ++i ) {
scanf ( "%d%d", &u, &v );
edg[ u ].push_back ( v );
edg[ v ].push_back ( u );
}
int mx = longestpath ();
while ( m-- ) {
int k;
scanf ( "%d", &k );
if ( k <= mx + 1 )
printf ( "%d\n", k - 1 );
else
printf ( "%d\n", mx + ( k - mx - 1 ) * 2 );
}
}
return 0;
}