DP 套 DP
感觉就是一种技巧层面的东西。
有的时候我们希望统计一类最优型问题,而求解最优的过程中又需要通过 DP 来解决,此时就可以使用到这个叫做 DP 套 DP 的技巧。具体而言我们可以把内层的 DP 值当成状态存入外层 DP 中进行转移。而且这一类问题的内层 DP 的值的种类不会太大,否则无法当成状态存储。一般具有的性质就是可以对相邻的 DP 值进行差分并进行存储。结合两道题具体来说。
P4590
经典题目。考虑求两个串的最长公共子序列的过程,令 \(f_{x,y}\) 为两个前缀的答案,那么有:
\[f_{x,y}=\begin{cases}
\max(f_{x-1,y},f_{x,y-1}&a_x\ne b_y)\\
f_{x-1,y-1}+1
\end{cases}
\]
而题目中第二个串的长度非常短,而这个 DP 有一个性质,即同一行来看差分值只可能有 \(0,1\) 两种可能。很好理解,因为新加入的这个字符肯定不会使得答案突然变化特别多,所以合法的 DP 值序列是 \(2^s\) 级别的,于是就可以暴力状压存储,转移的时候解压转移即可。第二个不能出现 NOI 的限制非常常规。
P8352
现在看来这两道题惊人地相似。
考虑求最大独立子集的过程,用 \(f_x/g_x\) 代表是否强制选择这个点的答案,于是非常暴力地可以用 \(f_{x,a,b}\) 表示子树 \(x\) 两个 DP 值分别是 \(a,b\) 的答案,这显然是无法通过的。于是考虑内层 DP 的性质,发现我们其实并不关心 \(b\) 的值具体是多少,因为我们转移过来的值只会是 \(a\) 或者 \(\max(a,b)\),于是思考能否快速存储上述的两个值,发现是可以的,因为有一个结论是说 \(b\le a+k\),原因是选了这个点之后其它点的贡献一定不会更优,于是就可以把状态缩到一个可以接受的范围。最后做一个树上背包即可。感觉还是很巧妙的。