开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第10天,点击查看活动详情
L - Tree
You are given a tree with nodes. The tree is rooted at . Define as the set of nodes in the subtree of . Call a subset of nodes good if and only if satisfies at least one of the following contidions: For all where , either or . For all where , both and . You need to partition all nodes of into several good subsets. Calculate the minimum number of subsets.
题目大意
给我们一棵树,问我们至少要将这个树分成几个点集,每个点集中的点应该符合下面两个条件
- 一个点集中的任意两点谁也不是谁的子树
- 反之
题目解法
看到这个题目的第一眼我们肯定想到的是将这棵树划分成一个一个长链,每一个长链都是一个符合条件一的点集,这时候我们将所有的长链排序我们从小到大去枚举,如果把当前长度的长链拆分成一个一个条件为2的点集时此时点集数量时多少,我们枚举一下计算答案即可
Code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define pb push_back
const int N = 1e6 + 10;
vector<ll> e[N], c;
ll a[N], f[N];
void dfs(int u) {
f[u] = 1;
for(auto &v : e[u]) {
dfs(v);
f[u] = max(f[u], f[v] + 1);
}
bool ok = false;
for(auto &v : e[u]) {
if(!ok && f[u] == f[v] + 1) {
ok = true; continue;
}else {
c.pb(f[v]);
}
}
}
void solve() {
int n; scanf("%d", &n);
c.clear();
for(int i = 1; i <= n; i++) e[i].clear(), f[i] = 0;
for(int i = 2; i <= n; i++) scanf("%d", a + i);
for(int i = 2; i <= n; i++) e[a[i]].pb(i);
dfs(1);
c.pb(f[1]);
sort(c.begin(), c.end());
ll res = c.size();
for(int i = 0; i < c.size(); i++) {
res = min(res, ll(c[i] + c.size() - i - 1));
}
printf("%lld\n", res);
}
int main() {
int T; scanf("%d", &T);
while(T--)
solve();
}