1 条题解
-
0
自动搬运
来自洛谷,原作者为

Marser
所有的苦难与背负尽头,都是行云流水般的此世光阴。搬运于
2025-08-24 22:22:04,当前版本为作者最后更新于2020-06-24 11:15:05,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
省选前最后一篇题解,感觉是个比较清新的贪心题。
Tweetuzki 神仙写了长链剖分和线段树,私以为不必要。题意
定义点集 为树的一个 独立集,当且仅当 。
给定一棵大小为 的树,求最大 独立集的大小。
。题解
首先证明一个重要结论:对于以 为根的子树,假设该子树的最大 独立集大小为 ,则 子树对 的父亲的贡献为 或 。
这个上界非常显然,不作赘述。
考虑证明这个下界。假设我们当前在求子树 的答案,正在考虑合并子树 的答案,我们令 分别表示之前已经选中的点中,深度最小点和非严格次小点, 分别表示子树 中深度最小点和非严格次小点,$d_1=dis(x,a),d_2=dis(x,b),d_3=dis(y,c),d_4=dis(y,d)$,显然存在 。
, 是 的公共祖先
同理,。考虑这样一个贪心策略:
- 如果 ,则子树 的所有节点都可以计入答案,取到上界 。
- 如果 ,
- 如果 ,在最终答案中删除 ,加入子树 的所有节点,方案仍然合法且最优,贡献为下界 。
- 如果 ,加入子树 除 以外的所有节点,方案仍然合法且最优,贡献为下界 。
对于后面两种策略,最优性不难证明。由于一定无法取到 ,因此只删去一个点一定是最优的。
对于 的情况,最终只选择了 三个点。由于 ,我们只需要证明 即可。
对于 的情况,最终选择了 三个点,我们只需证明 。
所以,每棵子树的贡献至少为 。
我们在 dfs 的过程中维护每棵子树最大 独立集的大小 ,以及所有取到最大值的方案中,深度最小的点的最大深度 。
- 当 时,,。
- 当 时,,。
合并完所有子树后,再判断一下能不能选根节点即可。时间复杂度 ,代码极为简单。
代码
#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; }
- 1
信息
- ID
- 5590
- 时间
- 1000ms
- 内存
- 128MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者