题目链接
西南科技大学第十三届程序设计竞赛B题
Description
小ZZ太喜欢吃糖果了,以至于每天都要花钱去买糖果,于是聪明的他决定在学校开糖果店 。
学校可以看成一个nn个节点的树,每个节点代表一栋建筑,相邻的建筑之间有一条长长的道路。
小ZZ想买下kk栋建筑来开糖果店,为了方便管理,这kk栋建筑必须是同一个联通块 。
定义一栋建筑 ii 到联通块 GG 的距离为 didi,didi 等于 ii 到 GG 中的建筑的距离的最小值;换言之, di=min(dis(i,p)|p∈G)di=min(dis(i,p)|p∈G),dis(i,p)dis(i,p) 表示点 ii 到点 pp 的树上简单距离。
小ZZ十分关心客户的分布情况,他想知道DG=max(di|1≤i≤n)DG=max(di|1≤i≤n) 。当然 DGDG 越小代表他的糖果店分布更合理。小 ZZ 想知道最小的 DGDG(GG是kk 栋建筑的的一个集合)。
Input
第一行包含一个整数 T (1≤T≤1000)T (1≤T≤1000) 代表测试组数,对于每一组测试:
第一行两个整数 nn,kk (1≤n≤105,1≤k≤n)(1≤n≤105,1≤k≤n) 。
接下来 n−1n−1 行,每一行三个整数 u,v,w (1≤u,v≤n,1≤w≤109)u,v,w (1≤u,v≤n,1≤w≤109) 表示建筑 uu 和建筑 vv 之间有一条长为 ww 的道路。
数据保证所有测试的 nn 的和不会超过 106106。
Output
对于每组测试,输出一行,包含一个整数,表示最小的 DGDG 。
2 5 2 1 3 1 3 5 7 2 3 1 2 4 5 5 2 1 3 1 3 5 7 2 3 1 2 4 9
6 8
Hint
第一组测试:
选择 3,53,5 此时DGDG 为 66。
第二组测试:
选择 2,42,4 此时DGDG 为 88。
题目是中文题,不再复述题意。
有N个点的树,选取其中K个点构成的联通块,使得到达其他点的最远距离最短。
思路:
我们先找到树的直径,然后在树的直径上找到树的中心,最后,我们一定要取树的中心,再通过树的中心向外展开,我们不断的选取(K-1)个目前的最大距离的点,最后得到的值一定是最小的。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define esp 1e-6
#define INF 0x3f3f3f3f3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int maxN = 1e5 + 7;
int N, K, head[maxN], cnt;
struct Eddge
{
int nex, to;
ll len, all;
Eddge(int a=-1, int b=0, ll c=0):nex(a), to(b), len(c) {}
}edge[maxN<<1];
inline void addEddge(int u, int v, ll w)
{
edge[cnt] = Eddge(head[u], v, w);
head[u] = cnt++;
}
int A, B;
ll maxx;
inline void dfs_B(int u, int fa, ll val)
{
if(val > maxx) { maxx = val; B = u; }
for(int i=head[u], v; ~i; i=edge[i].nex)
{
v = edge[i].to; ll c = edge[i].len;
if(v == fa) continue;
dfs_B(v, u, val + c);
}
}
int fa[maxN];
ll to_fa_dis[maxN];
inline void dfs_A(int u, int up, ll val)
{
if(val > maxx) { maxx = val; A = u; }
for(int i=head[u], v; ~i; i=edge[i].nex)
{
v = edge[i].to; ll c = edge[i].len;
if(v == up) continue;
fa[v] = u; to_fa_dis[v] = c;
dfs_A(v, u, val + c);
}
}
int mid; ll minn;
inline void dfs_mid(int u, ll val)
{
ll QWQ = max(val, maxx - val);
if(QWQ < minn) { minn = QWQ; mid = u; }
if(fa[u] == -1) return;
dfs_mid(fa[u], val + to_fa_dis[u]);
}
ll dfs_sum_edge(int u, int rt)
{
ll ans = 0;
for(int i=head[u], v; ~i; i=edge[i].nex)
{
v = edge[i].to;
if(v == rt) continue;
ll tmp = dfs_sum_edge(v, u) + edge[i].len;
ans = max(ans, tmp);
edge[i].all = tmp;
}
return ans;
}
struct node
{
int u;
ll len;
node(int a=0, ll b=0, ll c=0):u(a),len(b) {}
friend bool operator < (node e1, node e2) { return e1.len < e2.len; }
};
priority_queue<node> Q;
bool inque[maxN];
inline void init()
{
cnt = maxx = 0;
for(int i=1; i<=N; i++) head[i] = fa[i] = -1;
while(!Q.empty()) Q.pop();
for(int i=1; i<=N; i++) inque[i] = false;
}
int main()
{
int T; scanf("%d", &T);
while(T--)
{
scanf("%d%d", &N, &K);
if(N == 1) { printf("0\n"); continue; }
init();
for(int i=1, u ,v, w; i<N; i++)
{
scanf("%d%d%d", &u, &v, &w);
addEddge(u, v, w);
addEddge(v, u, w);
}
dfs_B(1, 0, 0); //此时已经知道了一端点B了,我们再去把A求出来
maxx = 0;
dfs_A(B, 0, 0); //这时候把A点也找到了,树的长度为maxx了
minn = INF;
dfs_mid(A, 0); //现在已经知道了中心了,只需要从中心出发去跑一遍它的直接方向上的边即可、不断拓展,存进优先队列
dfs_sum_edge(mid, 0); inque[mid] = true; //必选的中心点。
for(int i=head[mid], v; ~i; i=edge[i].nex)
{
v = edge[i].to;
inque[v] = true;
ll all = edge[i].all;
Q.push(node(v, all));
}
for(int j=1; j<K; j++)
{
node temp = Q.top(); Q.pop();
int u = temp.u;
for(int i=head[u], v; ~i; i=edge[i].nex)
{
v = edge[i].to;
if(inque[v]) continue;
ll all = edge[i].all;
Q.push(node(v, all));
inque[v] = true;
}
}
if(Q.empty()) printf("0\n");
else
{
node temp = Q.top();
printf("%lld\n", temp.len);
}
}
return 0;
}