CF1881F Minimum Maximum Distance 题解 贪心+DFS

Minimum Maximum Distance

传送门

You have a tree with n n n vertices, some of which are marked. A tree is a connected undirected graph without cycles.

Let f i f_i fi denote the maximum distance from vertex i i i to any of the marked vertices.

Your task is to find the minimum value of f i f_i fi among all vertices.

For example, in the tree shown in the example, vertices 2 2 2, 6 6 6, and 7 7 7 are marked. Then the array f ( i ) = [ 2 , 3 , 2 , 4 , 4 , 3 , 3 ] f(i) = [2, 3, 2, 4, 4, 3, 3] f(i)=[2,3,2,4,4,3,3]. The minimum f i f_i fi is for vertices 1 1 1 and 3 3 3.

Input

The first line contains an integer t t t ( 1 ≤ t ≤ 1 0 4 1 \le t \le 10^4 1t104) — the number of test cases.

The first line of each test case contains two integers n n n and k k k ( 1 ≤ k ≤ n ≤ 2 ⋅ 1 0 5 1 \le k \le n \le 2 \cdot 10^5 1kn2105) — the number of vertices in the tree and the number of marked vertices, respectively.

The second line of each test case contains k k k integers a i a_i ai ( 1 ≤ a i ≤ n , a i − 1 ≤ a i 1 \le a_i \le n, a_{i-1} \le a_i 1ain,ai1ai) — the indices of the marked vertices.

The next n − 1 n - 1 n1 lines contain two integers u i u_i ui and v i v_i vi — the indices of vertices connected by the i i i-th edge.

It is guaranteed that the sum of n n n over all test cases does not exceed 2 ⋅ 1 0 5 2 \cdot 10^5 2105.

Output

For each test case, output a single integer — the minimum value of f i f_i fi among all vertices.

Examples

input #1

6
7 3
2 6 7
1 2
1 3
2 4
2 5
3 6
3 7
4 4
1 2 3 4
1 2
2 3
3 4
5 1
1
1 2
1 3
1 4
1 5
5 2
4 5
1 2
2 3
1 4
4 5
10 8
1 2 3 4 5 8 9 10
2 10
10 5
5 3
3 1
1 7
7 4
4 9
8 9
6 1
10 9
1 2 4 5 6 7 8 9 10
1 3
3 9
9 4
4 10
10 6
6 7
7 2
2 5
5 8

output #1

2
2
0
1
4
5

input #2

3
6 1
3
1 2
1 3
3 4
3 5
2 6
5 3
1 2 5
1 2
1 3
2 4
3 5
7 1
2
3 2
2 6
6 1
5 6
7 6
4 5

output #2

0
2
0

题目翻译

你有一棵树,树上有 n n n 个顶点,其中一些顶点被做了标记。树是没有循环的连通无向图。

f i f_i fi 表示顶点 i i i 到任何一个有标记顶点的最大距离。

你的任务是找出所有顶点中 f i f_i fi 的最小值。

例如,在示例所示的树中,顶点 2 2 2 6 6 6 7 7 7 已被标记。然后是数组 f ( i ) = [ 2 , 3 , 2 , 4 , 4 , 3 , 3 ] f(i) = [2, 3, 2, 4, 4, 3, 3] f(i)=[2,3,2,4,4,3,3] 。顶点 1 1 1 3 3 3 的最小值 f i f_i fi

输入格式

第一行包含一个整数 t t t ( 1 ≤ t ≤ 1 0 4 1 \le t \le 10^4 1t104 ) - 测试用例的数量。

每个测试用例的第一行包含两个整数 n n n k k k 1 ≤ k ≤ n ≤ 2 ⋅ 1 0 5 1 \le k \le n \le 2 \cdot 10^5 1kn2105 )–分别是树中的顶点数和标记顶点数。

每个测试用例的第二行包含 k k k 个整数 a i a_i ai ( 1 ≤ a i ≤ n , a i − 1 ≤ a i 1 \le a_i \le n, a_{i-1} \le a_i 1ain,ai1ai ) - 标记顶点的索引。

接下来的 n − 1 n - 1 n1 行包含两个整数 u i u_i ui v i v_i vi --由 i i i /th边连接的顶点的索引。

保证所有测试用例的 n n n 之和不超过 2 ⋅ 1 0 5 2 \cdot 10^5 2105

输出格式

对于每个测试用例,输出一个整数,即所有顶点中 f i f_i fi 的最小值。

以上来自 C o d e F o r c e s ,翻译: D e e p L 以上来自CodeForces,翻译:DeepL 以上来自CodeForces,翻译:DeepL

解题思路

前置知识

正文

(<-你自己的),拿好可以在粗糙物体表面留下痕迹的棍状物体,在一粗糙平面模拟样例(简单来说就是手动模拟样例)可以发现一个性质:这两个点都是红点间的路径上的,且最后的答案就是最大的红点间的间距的长度除以二上取整。

所以正解思路就为:找出最大的红点间的间距,再除以二向上取整。

暴力枚举复杂度为 O ( n 2 ) O(n^2) O(n2),显然不可行。

找到间距可以参考求树的直径的方法,两遍 D F S DFS DFS 即可。时间复杂度 O ( n ) O(n) O(n)

注意:当树上只有一个标记点时,答案为 0 0 0,要直接特判输出,不然会出问题(血淋淋的痛啊)。

AC Code

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int Maxn = 2e5 + 5;
int n, k, a[Maxn];
int vis[Maxn], f[Maxn], dis[Maxn];
vector<int> e[Maxn];
int ans;
inline void dfs1(int x, int fa);
inline void dfs2(int x, int fa);
inline void solve();
inline void work() {
	int T;
	cin >> T;
	while (T--)solve();
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	work();
	return 0;
}
inline void dfs1(int x, int fa) {
	if (fa != -1)f[x] = f[fa] + 1;
	for (auto i : e[x]) {
		if (i == fa)continue;
		dfs1(i, x);
	}
}
inline void dfs2(int x, int fa) {
	if (fa != -1)dis[x] = dis[fa] + 1;
	for (auto i : e[x]) {
		if (i == fa)continue;
		dfs2(i, x);
	}
}
inline void solve() {
	cin >> n >> k;
	for (int i = 1; i <= n; i++)e[i].clear(), dis[i] = f[i] = a[i] = 0;
	for (int i = 1; i <= k; i++)cin >> a[i];
	int u, v;
	for (int i = 1; i <= n - 1; i++)cin >> u >> v, e[u].push_back(v), e[v].push_back(u);
	if (k == 1) {
		cout << 0 << endl;
		return;
	}
	f[0] = -1, dis[0] = -1, dfs1(1, -1), ans = a[1];
	for (int i = 1; i <= k; i++)if (f[a[i]] > f[ans])ans = a[i];
	dfs2(ans, -1), ans = a[1];
	for (int i = 1; i <= k; i++)if (dis[a[i]] > dis[ans])ans = a[i];
	cout << (dis[ans] + 1) / 2 << endl;
}

洛谷评级: 普及 / 提高 − \textcolor{orange}{普及/提高−} 普及/提高,适合新手宝宝体质哦(颖音)。

  • 18
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值