「典」一种简单限制树上背包的优化
给定一棵 个点的树,第 个点上有一个体积为 ,价值 的物品,每个点为黑色或者白色。
在树上选择若干个点,要求每个被选的点的最近的被选的祖先要和他异色,并且体积和 ,求最大价值和。
需要对原树的每棵子树求出答案。
。
不同于最简单的树上背包,这个每个点的体积是给出的,因此暴力复杂度为 的。
一个小优化:只把有效点值存下来然后暴力转移,复杂度分析一下可以得到 ,还是有点用的。
直接放弃掉树上背包。
然后想到了约束背包经典题目,古典做法就是把 dfs 序跑出来,然后在 dfs 序上 dp,一个点如果不选了那么它的子树就都不会选了,直接跳到下一个地方,否则就向子树跳一个,这样的话只需要支持背包加入一个物品,那么复杂度就是 。
如果跑出任意一个连通块,需要加一个点分治,但是子树内答案就很难做(好像不可做)。
然后有了一个最新科技,感觉完全吊打 dfs 序上 dp。
dfs 序上 dp 的本质是,类似建一个自动机,每个点连向第一个儿子或者子树外的第一个点,然后自动机上的一条路径就代表了一个树上包含根的连通块,然后在这个上面 dp。
但是这个东西和子树内信息非常割裂,而且只能维护树上连通块,遇到树上非联通块的约束条件就萎了,就是 dp 到一个点,父亲的信息是未知的。
此时一个很牛的考虑是,把 dfs 到 的背包信息传到儿子里面去 dp,然后再传出来。
1 | function dfs(c, dp)//贺的 |
由于外部点怎么选对我其实是没有影响的,只需要把外面的 dp 数组搬过来直接做,约束条件可以在传回父亲的时候进行限制即可。
然后每次传回父亲的时候再将这个点加入。
注意到这个 dp 复杂度是 ,很不牛。
注意到复杂度劣主要是因为每个子树都分成 0/1 两种情况分别 dp,注意到如果只有一个儿子,那么可以把儿子的 dp 状态值直接继承上来,因为没有其他儿子的约束。
首此启发,考虑一个类似 dsu on tree 的东西,先递归重儿子,把信息直接继承。
1 | function dfs(c, dp) |
分析一下复杂度。
如果儿子有 个,那么最大值一定是 。
显然 最优,复杂度为 。
但是注意到现在得到的这个 dp 还是只能求出整棵树的答案,不能求出每个子树的答案,因为每次递归都是把外面的信息传进去。
求一个点的信息,只要传一个空的状态进去就行了,但是复杂度乘个 。
这个也可以 dsu,把空信息传到重子树里面,然后整个传回来,然后扔到轻子树里面去 dp。
复杂度类似的分析一下,发现和求单次是一样的。
和上面一题相同,但是要求改成选出的点集是独立集。
nfls 讲课的题,标算是一个引荐东西,注意到两个点有边,那么在点分树上一定是祖先关系,而点分树的树高有保证,所以状压记录祖先的选择情况,复杂度是 的,不牛。
fxt 讲了一下 的做法,不牛。
直接套用上面的做法就可以了,贴一个代码:
1 |
|