2022ICPC西安站 L. Tree

504 阅读1分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第10天,点击查看活动详情

L - Tree

You are given a tree TT with nn nodes. The tree is rooted at 11. Define subtree(u)\mathrm{subtree}(u) as the set of nodes in the subtree of uu. Call a subset of nodes SS good if and only if SS satisfies at least one of the following contidions: For all u,vSu, v\in S where uvu\neq v, either usubtree(v)u\in \mathrm{subtree}(v) or vsubtree(u)v\in \mathrm{subtree}(u). For all u,vSu, v\in S where uvu\neq v, both usubtree(v)u\notin \mathrm{subtree}(v) and vsubtree(u)v\notin \mathrm{subtree}(u). You need to partition all nodes of TT 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();
}