持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第10天,点击查看活动详情
题目连接
题目大意
题目描述
给你一棵有根树,由 个顶点组成。顶点编号从 1 到 n,根是顶点 1。
最多可以执行以下操作 次:
-
选择树的边 ,使得 v 是 u 的父亲节点;
-
删除边 ;
-
添加一条边 (即,使 u 及其子树成为根的子树)。
定义树的高度是它的所有顶点中深度最大的顶点的深度,而顶点的深度是从根到它的路径上的边数。例如,顶点 1 的深度为 0,因为它是根,顶点 1 所有子节点的深度为 1。
执行 次操作后可以达到的树的最小高度是多少?
输入描述
第一行包含一个整数 ,表示测试用例的数量。
每个测试用例的第一行包含两个整数 和 ——树中的顶点数和您可以执行的最大操作数。
第二行包含 n−1 个整数 ,表示第 i 个顶点的父节点。顶点 1 是树的根。
所有测试用例的 n 总和不超过 。
输出描述
对于每个测试用例,打印一个整数 - 通过执行最多 k 操作可以实现的树的最小高度。
思路
易知操作之间不相互依赖,我们可以自由地重新安排操作。
我们单次操作不会影响到点 u 子树外其他点的深度。想象一下,如果我们按照顶点的深度用大到小进行操作,即可避免先操作了点 u,后操作以 u 为根节点的子树中的点的情况。
如果我们知道我们想要得到什么高度 h,我们切割下了点 u 并将它连接到了根节点上,那么以节点 u 为根节点的子树中的点距离点 u 的边数一定不超过 h-1,否则重新连接后该子树中就会存在点的深度大于 h,与我们之前决定的策略不符。所以当节点 u 被切下,其子树中所有的节点都可以看做不存在。
最多需要多少次操作才能使树的高度达到 h?显然,这个问题的答案随着 h 的增加而减少。因此,我们将能够对其应用二分来找到我们最多 k 次操作可以达到的最小高度。
当我们二分到一个答案 h 之后,我们要反复选择高度最多为 h-1 的子树并将它们切断,直到树的高度变为最多 h。显然最佳策略是切割最深顶点的第 h-1 层父节点,因为最深顶点到它的第 h-1 层父节点之间必然需要选择一个节点将其切下,我们希望使得剩下的节点深度尽可能小。
代码
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#include <string.h>
#include <queue>
using namespace std;
const int N=200001;
const long long mod=1000000007;
// const long long mod=998244353;
struct asdf{
int dep,id;
}a[N];
int n,m,k,f[N];
vector<int> e[N];
int cmp(asdf a,asdf b)
{
return a.dep<b.dep;
}
void dfs(int u)
{
for (auto v:e[u])
{
a[v].dep=a[u].dep+1;
dfs(v);
}
}
int vv[N];
void set0(int u)
{
vv[u]=1;
for (auto v:e[u])
{
if (vv[v]) continue;
set0(v);
}
}
int check(int h)
{
int cnt=0;
for (int i=1;i<=n;++i) vv[i]=0;
for (int i=n,g;i>=1;--i)
{
if (vv[a[i].id]) continue;
if (a[i].dep<=h) break;
g=a[i].id;
for (int j=1;j<=h-1;++j) g=f[g];
set0(g);
cnt++;
}
return cnt<=k;
}
int T;
int solve()
{
for (int i=1;i<=n;++i) e[i].clear();
scanf("%d",&n);
scanf("%d",&k);
for (int i=1;i<=n;++i) a[i].id=i;
for (int i=2;i<=n;++i)
{
scanf("%d",&f[i]);
e[f[i]].push_back(i);
}
a[1].dep=0;
dfs(1);
sort(a+1,a+1+n,cmp);
int l=1,r=n,mid;
while (l!=r)
{
mid=l+r>>1;
if (check(mid)) r=mid;
else l=mid+1;
}
printf("%d\n",l);
return 0;
}
int main()
{
for (scanf("%d",&T);T--;) solve();
return 0;
}