分治优化
概述
-
分治优化常常在 dp 的转移有某种单向单调性时使用,通过类似整体二分的结构,确保每个决策点只在一条链上出现,从而加速转移。一般这种分治优化也有对应的二分栈形式,区别仅在于逆推和顺推。此部分在四边形不等式优化一节。
-
分治优化本身还是一种非常有趣的 dp 实现方式。这一部分...我还不是非常了解,也是本节的重点。但似乎...这就是线段树分治?(如果确实,考虑将这边合并过去,毕竟这种分值处理的可不仅仅是 dp)。
例题
CF1442D Sum
-
题意略。下面的复杂度分析里,因为 \(n,K\) 同阶,用 \(n\) 代替 \(K\)。
-
发现这个就是把我们广义多重背包的物品反了过来,更短的转移反而“性价比”更高。考虑利用四边形不等式。
-
那么起式子,\(dp_{i,j}=\max_{k\leqslant j}(dp_{i-1,k}+v_{i,j-k})\)。这里不写那种四边形形式了,容易看出,交叉小于包含。
-
当然 dp 的转移来源还是要推一下,盲猜有 \(k_{j-1}\geqslant k_j\),理由是取 \(l<l'\leqslant r<r'\),假设有 \(k_r=l,k_{r'}=l'\),推一下试试:
-
\(dp_{i-1,l}+v_{r-l}+dp_{i-1,l'}+v_{r'-l'}\geqslant dp_{i-1,l}+v_{r'-l}+dp_{i-1,l'}+v_{r-l'}\)。
-
左右消一下,得到 \(v_{r-l}+v_{r'-l'}\geqslant v_{r'-l}+v_{r-l'}\),显然和交叉小于包含矛盾了。
-
-
那完事了,\(O(n^2\log)\)?等等,我怎么觉得这个结论很反直觉?
-
令 \(j-1=0\),则 \(k_{j-1}=0\),从而...从而......\(k=0\)。显然不可能啊摔!
-
好吧,我们再仔细看看式子:上面的推导只能说明 \(dp_{i-1,l}+v_{r-l}+dp_{i-1,l'}+v_{r'-l'}\leqslant dp_{i-1,l}+v_{r'-l}+dp_{i-1,l'}+v_{r-l'}\),然而这一式子不能推出 \(k_{j-1}\geqslant k_j\),其只是 \(k_{j-1}\geqslant k_j\) 的必要条件。
-
...好吧。我们回到一开始的性价比,有一个直觉是这样的:只会有一组不选完,毕竟越选越划算,如果靠后的没选完的那一组更划算,则应该都选那组,否则都选这组。
-
形式化地说,设有两组物品 \(A,B\) 都没选完,不妨认为分别选了 \(k_A,k_B\) 个,则:
-
若 \(A_{k_A+1}\geqslant B_{k_B}\):放弃取 \(B\),尽量取 \(A\),显然不更劣。
-
若 \(A_{k_A+1}<B_{k_B}\):这代表着 \(A_{k_A}<B_{k_B}\),于是放弃取 \(A\),尽量取 \(B\),显然更优。
-
-
OK!那么这样一来,除了我们钦定的那一组之外,都变成 \(01\) 背包了,要么不选要么选完。然而...暴力枚举哪一组不选完?组内的 dp 容易预处理,毕竟就是前缀和;然而即使我处理好前后缀 dp,这一部分的复杂度是 \(O(n^2)\),把三个背包暴力合到一起...虽然合两个其实就够了(第二次不用合,因为只关心 \(dp_K\)),但 \(O(n^2)\) 做 \(n\) 遍,还是暴力复杂度啊。
-
解法 1:分块。
-
分 \(B\) 个块,暴力枚举不选完的在哪个块里,合并前后缀 DP 或直接暴力 DP 一遍,反正都是 \(O(n^2)\)。
-
在块内暴力枚举哪个没选完,将其他的物品暴力转到刚才的背包里,\(O(\frac{n}{B}(\frac{n}{B}n))\)。
-
暴力枚举没选完的选了几个,计算对应情况下选 \(K\) 个的最大总价值,\(O(n)\)。
-
故总复杂度为 \(O(B(n^2+\frac{n}{B}(\frac{n^2}{B}+n)))=O(Bn^2+\frac{n^3}{B})=O(n^2(B+\frac{n}{B}))\),取 \(B=\sqrt{n}\),复杂度为 \(O(n^2\sqrt{n})\)。
-
够过了,虽然纸面 \(5\times 10^8\),但背包常数小。
-
-
解法 2:分治。
-
考虑用二分树。怎么到处都是二分树!也许该叫分治树?
-
令初始区间为 \([1,n]\),先考虑 \(key\leqslant mid\) 的情况,\(key\) 为不选完的一组,此时将 \([mid+1,r]\) 的物品转进背包得到左子区间的背包,回溯时再考虑 \(key>mid\),将 \([l,mid]\) 转进背包。
-
即我们维护的背包其实是“除了当前区间外的全局背包”。可以看出,同时只需要维护一条链上的 \(\log\) 个背包(转移完毕的就好),每一层都将所有物品转移一次,空间回收的复杂度因为分治树只有 \(O(n)\) 个点所以是 \(O(n^2)\),故总复杂度 \(O(n^2\log)\)。
-
妙哉!
-
P4095 [HEOI2013] Eden 的新背包问题
-
题意略。
-
显然可以和上一题一样建分治树,\(O(nV\log)\) 地解决。我就不写了吧(以前写了个前后缀合并的假做法,时间是假的)...毕竟单调队列优化多重背包码量还是不小的。