P3780 [SDOI2017] 苹果树-题解

神题

intro: 一般的树形dp要么是从父亲往儿子转移,或儿子往父亲转移,但在这题,你会见到两种转移

先转化题意为树上背包+选择一条链不计入取的苹果数
可以发现一定选择从根到叶子的一条链

如果选定了一条链,答案的构成就是:链上免费 + 链上额外取的答案 + 链左右两边取的答案

\(f[u][k]\) 表示链底为\(u\) 正序取(包括链上)取了\(k\) 个苹果的最大幸福度

\(g[u][k]\) 表示链底为\(u\) 逆序取(不包括链上)取了\(k\) 个苹果的最大幸福度

\(d[u]\) 表示\(u\) 到根免费取苹果的幸福度和

\(ans=max\left \{f[i][j]+f[i][k-j]+d[i]\right \}\)

如何转移?
对于一个结点\(u\),设它的父亲为\(fa\),第一次访问到\(u\) 时,先继承\(f[fa]\),然后再用\(a_u-1\) 个苹果做多重背包更新\(f[u]\)

但是这样\(u\) 的兄弟就不能直接继承父亲了,好在我们一定用所有的叶子去更新最终答案,所以还可以再利用非叶子的\(f\) 数组

当处理完\(u\) 的子树后,用\(f[u]\) 再去更新\(f[fa]\) 此时的\(u\)不在免费的链上,因为接下来它的兄弟在免费的链上

取苹果具有依赖性,对于\(fa\) 要么选择\(u\) 子树,要么不选择,所以
\(f[fa][k]=max \left \{f[fa][k],f[u][k-1]+v[u]\right \}\)

总结: 先复制\(f[fa]\)\(f[u]\),然后对\(f[u]\) 做多重背包,再递归处理\(u\)的子树,回溯到\(fa\)时更新\(f[fa]\)

对于\(g\),逆序遍历树,套用转移\(f\)的代码,再把做多重背包放到最后一步即可

tips:

  1. 多重背包转移使用单调队列优化
  2. 多重背包转移时要使用一个辅助数组复制一遍\(f\),防止利用已转移的\(f[k]\) 进行错误的转移
  3. \(f\)\(g\) 最好开一维卡常
  4. \(index(i,j)\)映射\(f[i][j]\) 应该为\((i-1)*(k+1)+j\) 因为可以是\(f[i][0]\)
  5. 由第四条,\(f\)\(g\) 的空间应该开\(n*(k+1)\)
  6. 再对\(g[u]\) 做多重背包之前就应该计算最终答案
posted @ 2026-08-01 20:52  floaya  阅读(2)  评论(0)    收藏  举报