多重背包&树上背包小结
多重背包&树上背包
多重背包
有 \(n\) 种物品,每种物品有 \(s_i\) 个,价值为 \(v_i\) ,体积为 \(w_i\) ,背包容量为 \(V\) ,问最大价值
二进制拆分
把 \(s\) 进行二进制拆分,然后就是01背包的过程,\(O(nV\log V)\)
可以用bitset优化
单调队列
对于每个物品,先枚举\(k=0\sim w_i-1\),然后枚举余数为k的数,这个过程是 \(O(V)\) 的
然后我们发现对于一个k,进行的是内部转移,于是就维护一个单调队列即可
时间复杂度 \(O(nV)\)
CF1856E2 PermuTree (hard version)
题目描述
给定一棵以 \(1\) 为根的有根树,你需要给出一个 \(1\) 到 \(n\) 的排列 \(a\),最大化二元组 \((u,v)\) 的数量,满足 \(a_u < a_{\rm {lca(u,v)}} < a_v\),输出这个最大值。(\(2 \leq n \leq 10^6\))
solution
可以考虑在lca处算答案,然后贪心,把儿子分成两个尽量相等的部分
于是就对儿子的子树大小进行背包,\(O(n^2)\)
然后我们发现因为一次计算的子树大小和为n,那么最多有 \(\sqrt n\) 种值,然后跑多重背包即可
注意,如果用二进制拆分,其实也没有log,但是有一个实现细节:我们把进行二进制拆分后的01物品放进桶里,然后一个桶里若有超过两个,就可以等价于二进制的进位,可以证明是物品时 \(\sqrt n\) 级别的。
然后还有个细节,用bitset要动态定义长度,所以可能需要手打
树上背包
树形背包总结 - lnzwz - 博客园 (cnblogs.com)
2023.10.11联考T4
题目描述
给定一棵 \(n\)个节点、以 \(1\) 为根的树。对于每一条边,可以选择保留或不保留。
定义一个方案的权值是:只看保留的边时,形成的各个连通块大小之积的 \(k\) 次方。试计算\(2^{n-1}\) 种方案的权值之和。
solution
计数好题
首先考虑暴力,设\(g_{i,j}\)表示以i为根的子树,i所在的联通块大小为j的代价,j=0时表示断掉i的父亲那条边,然后为了方便转移,我们i所在连通块的代价先不算,每次更新完一棵子树,再进行更新\(g_{i,0}+=\sum g_{i,j}\times j^k\)
然后我们发现可以背包转移,时空复杂度\(O(n^2)\)
想到这里发现根本无法优化,于是就需要一点经验,发现\(k\le100\)
设\(f_{i,j}=\sum_{s=0}^{siz_i}(^s_j)f_{i,s}\),就是以i为根的子树,i所在的联通块贡献\((^s_j)\)的代价,我们发现,j的范围被缩减成k,然后根据第二类斯特林数的公式(普通幂转下降幂),\(s^k=\sum_{i=1}^ks2(k,i)i!(^s_i)\),然后令\(ans_i\)为该点断掉父亲的答案,\(ans_i=\sum_{j=1}^ks2(k,j)j!f_{i,j}\)
发现了可以计算答案,那么如何转移呢?
考虑状态的组合意义,在连通块中取了j个点,由于子树中的情况互不影响,可以用乘法原理,于是又可以背包转移,注意ans要当作大小为0的物品转移
然后如果我们把背包大小定为\(min(k,siz_i)\),时间复杂度为\(O(nk)\)
2023.12.24联考T1
题目大意
给定一棵树,每个节点有个01权值,一棵树的贡献定义为满足 \(x\) 为 \(y\) 的祖先,且\(s_x=1,s_y=0\) 的 \((x,y)\) 的数量
现在可以反转k个权值,问最小贡献。对于 \(k=0\sim n\) 都输出答案
solution
首先考虑如果要把1改成0,那么要求它的子树一定都为1,否则不如改子树
0改成1同理,要求它到根都为0
所以可以考虑一个朴素的dp,设\(f_{i,j,k}\)表示节点i的子树,用了j个反转,下面有k个0,背包转移\(O(n^3)\)
在根据上面的结论,设\(f_{i,j,0/1/2}\)表示节点i的子树,用了j个反转,子树一定都为1/它到根都为0/不确定