题解 P6574 【[BOI 2017] Cat in a tree】
省选前最后一篇题解,感觉是个比较清新的贪心题。
Tweetuzki 神仙写了长链剖分和线段树,私以为不必要。
题意
定义点集
给定一棵大小为
同理,
考虑这样一个贪心策略:
- 如果
d_1+d_3+1\ge k ,则子树y 的所有节点都可以计入答案,取到上界f_y 。 - 如果
d_1+d_3+1 < k ,- 如果
d_3+1>d_1 ,在最终答案中删除a ,加入子树y 的所有节点,方案仍然合法且最优,贡献为下界f_y-1 。 - 如果
d_3+1\le d_1 ,加入子树y 除c 以外的所有节点,方案仍然合法且最优,贡献为下界f_y-1 。
- 如果
对于后面两种策略,最优性不难证明。由于一定无法取到
对于
对于
所以,每棵子树的贡献至少为
我们在 dfs 的过程中维护每棵子树最大
- 当
dep_x+dep_y+1\ge k 时,dep_x\gets \min(dep_x,dep_y+1) ,f_x\gets f_x+f_y 。 - 当
dep_x+dep_y+1< k 时,dep_x\gets \max(dep_x,dep_y+1) ,f_x\gets f_x+f_y-1 。
合并完所有子树后,再判断一下能不能选根节点即可。时间复杂度
代码
#include<bits/stdc++.h>
#define reg register
typedef long long ll;
using namespace std;
const int MN=2e5+5;
int to[MN<<1],nxt[MN<<1],h[MN],cnt;
inline void ins(int s,int t){
to[++cnt]=t;nxt[cnt]=h[s];h[s]=cnt;
to[++cnt]=s;nxt[cnt]=h[t];h[t]=cnt;
}
int n,m,f[MN],dep[MN];
void dfs(int st,int fa=0){
dep[st]=1e9;
for(reg int i=h[st];i;i=nxt[i])
if(to[i]!=fa){
dfs(to[i],st);
if(dep[st]+1+dep[to[i]]>=m){
f[st]+=f[to[i]];
dep[st]=min(dep[st],dep[to[i]]+1);
}
else{
f[st]+=f[to[i]]-1;
dep[st]=max(dep[st],dep[to[i]]+1);
}
}
if(dep[st]>=m)f[st]++,dep[st]=0;
}
int main(){
scanf("%d%d",&n,&m);
for(reg int i=2,x;i<=n;i++)
scanf("%d",&x),ins(i,x+1);
dfs(1);printf("%d\n",f[1]);
return 0;
}