古典叙事 · 技术札记

序章

山河有卷
人间有声

写代码,也写长风、旧城与未熄的灯。
愿每一篇随笔,都有自己的山水与回声。
阅览随笔

CF1695D2 Tree Queries (Hard Version)

CF1695D2 Tree Queries (Hard Version)

题目链接https://codeforces.com/contest/1695/problem/D2

题目描述

此问题与CF1695D1 Tree Queries (Easy Version)的唯一区别是树的大小的限定。

给定一棵无根树,有 $ n $ 个顶点。在这棵树上有一个顶点 $ x $ ,你希望找到它。

要找到 $ x $ ,你可以进行 $ k $ 次查询 $ v_1 , v_2 ,\ldots , v_k $ (其中 $ v_i $ 是树中的各个顶点)。当你进行完所有查询后,你会得到 $ k $ 个数字 $ d_1 , d_2 ,\ldots , d_k $ ,( $ d_i $ 是 $ v_i $ 和 $ x $ 之间的最短路径上的边数)。注意,您知道哪个距离对应于哪个查询。

请你求出最小的 $ k $ ,使存在这样的一些查询 $ v_1 , v_2 ,\ldots , v_k $ ,无论哪一个节点 $ x $ (无论 $ x $ 是什么),你都能通过查询 $ v_1 , v_2 ,\ldots , v_k $确定节点 $ x $。

注意,你不需要输出这些查询。

输入格式

每个测试点包含多组数据。

第一行为数据的组数 $ t $ ( $ 1 \le t \le 1e4 $ )。

对于每组数据的描述如下。

每组数据的第一行包含一个整数 $ n $ ( $ 1 \le n \le 2e5 $ ) 即树中的顶点数。

然后有 $ n - 1 $ 行,每行包含两个整数 $ x $ 和 $ y $ ( $ 1 \le x, y \le n $ ),意味着树中顶点 $ x $ 和 $ y $ 之间有一条边。

数据保证所给的边一定形成一棵树。

数据保证每个测试点中 $ n $ 的总和不超过 $ 2e5 $ 。

输出格式

对于每组数据,在此行输出一个非负整数,即为你需要的最小查询数。(即每一行对应输出一个查询数字)

输入输出样例

样例输入 #1

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

样例输出 #1

0
1
2

输入输出样例 #1

输入 #1

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

输出 #1

0
1
2

说明/提示

在第一组数据中,只有一个顶点,因此不需要任何查询。

在第二组数据中,你可以进行关于节点 $ 1 $ 的单个查询从而得到 $ x $ 节点的信息。

思路

首先,题目要求我们在树上找k个点,知道这k个点到其他任意点x的路径长度,我们可以知道x是哪一个点,求k的最小值
我们来分析一下第三组样例:

image
假如我们选择点1和9,就得到了节点1和节点9到节点x的距离,就可以知道x是树上确定的唯一节点,所以最小值是2
我们发现,如果是一个链状,那么一定通过一个节点就可以确定其他节点。
如果这个节点的度数大于3,那么这个节点的子树中的所有叶子节点(假如有n个)必须选出来n-1个才可以表示这个子树上所有的点,。
例如一个菊花图
image
我们必须要选出四个叶子节点才能表示这一个整个图。
所以我们每次找叶子节点,然后往上走,找到第一个度数大于3的节点,那么这个节点的子树就都可以用这个节点子树的叶子节点数量-1表示。
所以就可以把这个题过了

AC代码
#include <iostream>
#include <vector>
using namespace std;
using i64 = long long;
i64 n;
vector<i64>t [ 200005 ];
bool vis [ 200005 ];
int dfs(int u,int fa){
	if(t[u].size()>2) return u;//节点度数大于3返回
	for(auto v:t[u]) if(v!=fa) return dfs(v,u);//不然继续找
	return u;
}

int main() {
	i64 m;
	cin >> m;
	while (m--) {
		cin >> n;
		i64 ans = 0;
		for (int i = 1; i <= n; i++) {
			t [ i ].clear();
			vis [ i ] = 0;
		}
		if (n == 1) {
			cout << 0 << endl;//特判一个节点的情况
			continue;
		}
		for (int i = 1; i < n; i++) {
			i64 x , y;
			cin >> x >> y;
			t [ x ].push_back(y);
			t [ y ].push_back(x);
		}
		for (int i = 1 , u , v; i <= n; i++) {
			if (t [ i ].size() == 1)
				vis [ dfs(i , 0) ] = 1 , ans++;//从叶子节点往上走
		}
		for (int i = 1; i <= n; i++) {
			if (vis [ i ])ans--;//表示的是该子树叶子节点数-1
		}
		if (ans == 0)cout << 1 << endl;//如果是链状特判一下
		else {
			cout << ans << endl;//输出答案
		}
	}
}

posted @ 2025-08-15 15:33  Morphis‘  阅读(22)  评论(0)    收藏  举报

特别策划 · CINEMATIC NOTES

风沙与孤骑

风起塞外,
胜负在刀剑之前。

“把复杂拆成秩序,把未知写成答案。”
Morphis · 山河一卷 愿你从这里出发,仍能听见山风。 影像:farfarSébastien Goldberg / Unsplash