Skip to content

JZOJ - 5455. 拆网线

检测到 KaTeX 加载失败,可能会导致文中的数学公式无法正常渲染。

#题面

#题目描述

企鹅国的网吧们之间由网线互相连接,形成一棵树的结构。现在由于冬天到了,供暖部门缺少燃料,于是他们决定去拆一些网线来做燃料。但是现在有 KK 只企鹅要上网和别人联机游戏,所以他们需要把这 KK 只企鹅安排到不同的机房(两只企鹅在同一个机房会吵架),然后拆掉一些网线,但是需要保证每只企鹅至少还能通过留下来的网线和至少另一只企鹅联机游戏。

所以他们想知道,最少需要保留多少根网线?

#输入格式

第一行一个整数 TT,表示数据组数;

每组数据第一行两个整数 N,KN, K,表示总共的机房数目和企鹅数目。

第二行 N1N - 1 个整数,第 ii 个整数 AiA_i 表示机房 i+1i + 1 和机房 AiA_i 有一根网线连接(1Aii1 \leq A_i \leq i)。

#输出格式

每组数据输出一个整数表示最少保留的网线数目。

#输入输出样例

样例输入 #1

2
4 4
1 2 3
4 3
1 1 1

样例输出 #1

2
2

#数据范围与约定

  • 对于 30%30\% 的数据,N15N \leq 15
  • 对于 50%50\% 的数据,N300N \leq 300
  • 对于 70%70\% 的数据,N2000N \leq 2000
  • 对于 100%100\% 的数据,T10T \leq 102KN1052 \leq K \leq N \leq 10^5

#思路

显然两两结对时的答案是最优的,那么需要尽可能多地找出这种点对。之后对于孤立点直接连接到附近的点对即可。

求点对时如果采用自上而下的方式去配对的话,可能会将上图中的 1,21, 2 点先组成一个点对,但显然这不是最好的配对方式。最好的配对方式应该是自下而上匹配,将 2,42, 41,31, 3 各自配对,只需要保留两根网线即可。

在匹配出所有的点对之后,如果仍未满足需求,再使用孤立点补齐即可。

#代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include <iostream>
#include <cmath>
#include <vector>

using std::cin;
using std::cout;
const char endl = '\n';

const int N = 1e5 + 5;

int t, n, k;
std::vector<int> top, g[N];
bool vis[N];

void dfs(int u) {
vis[u] = true;

for (int v : g[u]) {
if (!vis[v]) dfs(v);
}

top.push_back(u);
}

int main() {
std::ios::sync_with_stdio(false);
cin.tie(nullptr);

cin >> t;

while (t--) {
top.clear();
std::fill_n(g, N, std::vector<int>());
std::fill_n(vis, N, false);

int ans = 0;

cin >> n >> k;

for (int i = 2, x; i <= n; i++) {
cin >> x;

g[i].push_back(x);
g[x].push_back(i);
}

dfs(1);

std::fill_n(vis, N, false);

for (int u : top) {
if (k <= 1) break;

if (vis[u]) continue;

for (int v : g[u]) {
if (vis[v]) continue;

vis[u] = vis[v] = true;
ans++;
k -= 2;
}
}

cout << ans + k << endl;
}

return 0;
}