想象一下(imagine)
想象一下(imagine)
题目描述
我们高大的老班举起了有半个他那么高的三角板,说:“你们想象一下——”
于是你就陷入了想象……
有一棵n个点的树,每个叶子节点上都有一个人,他们按照每秒钟走一条边的速度向树根(节点1)前进。
你可以运用k次想象之力,让某一个节点(除了根节点)上的所有人瞬间(耗时为0)转移到这个节点的父亲上。
你想知道最少需要多少时间,所有人可以到达根节点。
输入
输入的第一行包含两个整数n,k,含义见问题描述。
接下来n-1行,第i行一个整数fai,表示节点i的父亲为fai。
输出
输出一行一个整数,表示所有人到达根节点最少需要的时间。
样例输入
<span style="color:#333333"><span style="color:#333333">【样例1输入】
6 2
1
2
2
2
4
【样例2输入】
3 2
1
2
</span></span>
样例输出
<span style="color:#333333"><span style="color:#333333">【样例1输出】
1
【样例2输出】
0</span></span>
提示
【样例1说明】
一开始,在节点3,5,6上分别有一个人,我们称他们为A,B,C。
时刻0,在节点6运用想象之力,A到达节点4。
第1秒,A,B,C走到节点2。
时刻1,在节点2运用想象之力,A,B,C到达节点1,即目的地。
共用时1秒。
【样例2说明】
一开始只有节点3上有一个人。
时刻0,在节点3运用想象之力,这个人到达节点2;
此时仍然为时刻0,在节点2运用想象之力,这个人到达节点1。
【子任务】
测试点 | n | k | 特殊性质 |
1 | ≤8 | <n | 无 |
2~4 | ≤100 | ||
5~8 | ≤3000 | ||
9 | ≤500000 | =1 | |
10 | <n | 树是一条链 | |
11~20 | 无 |
solution
考场时的想法:答案是有单调性的,那我二分一个mid,然后把所有点往上跳mid步
在用树形dp看看是否合法
效率O(nlog2) 90分
然而这题有O(n)做法
贪心把所有叶子往上跳,如果剩下的边不足k条,就break
因为想象应该越晚用越好(一次拉多个)
好吧说实话我也不太会证
1.一个节点最多只会使用1次想象之力(当最后一个人经过它的时候)
2.对于一个人来说,对他用的想象之力一定越靠近根越好(尽可能多的与其它点共用)。
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define maxn 500005
using namespace std;
int n,K,f[maxn],flag[maxn],s[maxn],ans;
struct node{
int x,bs;
};
queue<node>q;
int main(){
cin>>n>>K;
for(int i=2;i<=n;i++){
scanf("%d",&f[i]);
s[f[i]]++;
}
for(int i=1;i<=n;i++)if(!s[i])q.push(node{i,0});
int sum=n-1;if(K==sum){puts("0");return 0;}
while(!q.empty()){
node a=q.front();q.pop();
sum--;if(sum<=K){ans=a.bs+1;break;}
s[f[a.x]]--;
if(!s[f[a.x]]){
node ne;ne.x=f[a.x];ne.bs=a.bs+1;
q.push(ne);
}
}
cout<<ans<<endl;
return 0;
}