【SDOI2017】苹果树

感觉出息了,从 2024 暑假开始接触这道题,今天才刚刚会。

link

题意

给出一棵树,每个节点上有 ai 个苹果,价值为 vi,如果一个点取了苹果那么父亲也要取,设取了 t 个苹果,取苹果的最大深度为 h,那么要求 thkk 给定),求最大价值。

弱化版问题

弱化版:tk

直接从子树层面进行背包的复杂度大概是 O(nk2) 的。没有任何优化空间。如果我们能将一般背包的合并过程变得能用单调队列进行优化,那么复杂度很可能就是对的。考虑一种新的 dp 方式,按照 dfn 进行 dp,同时每次加入一个点时用单调队列优化多重背包。

我们将背包从子树层面搬到 dfn 层面,优化了复杂度。这是由于不需要支付子树合并的成本。考虑什么时候能做这样的变换,如果能通过一些手段使得子树内不需要承担过多的状态信息(该题利用下面的手段后子树不需要承担任何状态信息)即可。优化的复杂度只要来源于用技巧快速插入一个点来取代暴力的子树合并。如果没有这种技巧也很难做到复杂度的优化。

比如下面的问题:

每个点有价值 vi,限制 limi,代表子树中选的点的数量不超过 limi,求选择一些点的最大代价。

此时子树就需要承担选几个的信息,无法很好地优化复杂度。

回到这道题。令 fi,j 代表考虑完所有 dfn i 的点,选择 j 个苹果的最大价值。考虑 i 的子树内是否选苹果:

  • 不选,此时从 fi+sizi,j 进行转移。

  • 选,此时往 fi+1 这个背包中插入 ai1 个价值为 vi 个苹果后转移过来。

重点关注于后一种。对于 fi+1 而言,i 的子树 p 必然是合法的(选择苹果的终点是子树的根),那么此时必须要选一个 vi 的苹果,然后其余 ai1 个苹果可以任选,插入到背包即可。

设插完以后得到的新背包是 fi+1,那么转移:fi,j=max(fi+sizi,j,fi+1,j1+vi)。利用单调队列优化多重背包做到 O(nk)

原版问题

原版:thk

这个式子等价于可以选一条端点为根的链,链上面免费取一个苹果。

一个经典套路是,我们令一个点的所有子树按顺序排列(随便钦定一个顺序)。那么对于一个从根到某个点的链,会将整棵树劈成两部分,左边的部分按照 dfs 时结束访问的顺序是连续的一段前缀,右边的部分按照倒着 dfs 结束访问的顺序是一段连续的后缀。于是我们按照刚才的方法处理出前缀背包和后缀背包,及该条链上的背包,对三个背包进行合并可以做到 O(nk2)

注意我们并不是将三个背包完全合并起来,而是只关注合并后第 k 个位置上的值(根据贪心不难证明),因此复杂度并非来源于做两次暴力的背包合并。

继续优化,考虑能否去掉“该条链上的背包”,显然是可以的。我们令当前点到根的路径上的每个点都只选了至多 ai1 个苹果即可。这样只需要 O(nk) 的时间复杂度完成背包合并。

单调队列优化多重背包

考虑一般的转移:fi,j=min(fi1,jkw+kv),对于 r[0,w),记 gr(i,p)=f(i,pw+r)

如果能求出来所有的 gr(i,p),那么可以复原出 fi,j

在求 gr(i,p) 之前先考虑一下对于固定的 igr(i,p) 的状态数,显然为 w×Ww=WW 为体积上限。

gr(i,p)=min0kmin(c,p)(f(i1,pw+rkw)+kv)

套路地,gr(i,p)=minmax(pc,0)kp(f(i1,kw+r)kv)+pv

hr(i,p)=gr(i,p)pv。则 gr(i,p)=minmax(pc,0)kp(hr(i1,k))+pv

关键思想就是将无关项分离。

时间复杂度为 O(W)

posted @   BYR_KKK  阅读(19)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示