编者:樱初音斗橡皮 以及只有几篇题解和一些咕咕咕的东西的blog(真的非常懒。。。咕咕咕)
2020/08/10 update:指出部分事实性错误并修复LaTeX错误。
最近花了许多时间来理解、调试动态 dp 的模板题,终于 AC 了。网上的 blog 看了很多,不过讲得都是一笔带过,让我等蒟蒻很迷茫。。因此决定写一篇看得懂的 blog。。
序列上的动态 dp 问题
1. 单次询问
我们都知道最大子段和问题。
题目链接
问题1. 已知长度为
解答:设
容易写出暴力代码:
ans
这是经典的动态规划入门例题。那么,如果是多次区间询问呢?
2. 区间询问
题目链接
问题2. 已知长度为
解答:(此处不考虑离线分治、维护区间和与前后缀最大和以及最大子段和等等神奇的解法)
我们把状态转移方程写在这里。
编者于 2020/08/10 注:以下所有状态转移方程和矩阵中,dp数组的含义与上文代码中相同,即 dp[i] 表示“以 i 为结尾的最大子段和”,而非所求的 [L,R] 区间内的最大子段和。求后者需要额外添加一维矩阵状态。
把它写成矩阵形式。
问:慢着,这个矩阵我看着好像不太对?
答:这里我们重新定义了矩阵的运算。
众所周知,
经过手算验证后,我们发现一个惊人的事实:这个新定义的矩阵运算同样满足结合律!即:
(事实上,如果对矩阵中的元素重新定义“乘”和“加”运算(在这里分别是加法和取
因此,我们可以把它写作
这样的矩阵有什么用呢?定义
对于一个询问
因此,可以使用线段树/平衡树(
(事实上,我们设
(值得注意的是,这样的矩阵存在单位元
这种将状态转移方程转化为矩阵乘积再用数据结构维护的方法,就是传说中的动态 dp。
3. 单点修改,区间询问
题目链接
问题3. 已知长度为
解答:前面我们提到过,矩阵
这样,问题就变成了单点修改矩阵,区间询问矩阵乘积。可用线段树/平衡树(
这样的做法,能不能推广到树上去呢?
4. 树上区间赋值,区间询问
题目链接
问题4. 已知节点数为
的节点点权赋值为
解答:把序列上的东西推广到树上,且树的形态不发生变化,第一个想到的就是树剖。
维护重链信息,我们仍然使用线段树/平衡树/分块。对原树进行树链剖分,修改时,把
其实是可以的。设
第一,
依此类推,由数学归纳法可证得
第二,
依此类推,由数学归纳法可证得
这样,我们就可以通过线段树/平衡树/分块的区间赋值操作, 线段树/平衡树
询问的时候,将询问链拆成
总时间复杂度:线段树/平衡树
等等!树上的询问链的问题,除了树链剖分还能想到什么?
对了!还有 LCT(Link-Cut-Tree)。LCT 同样可以维护动态 dp。节点维护单点矩阵和 splay 的子树中矩阵乘积,每次链修改/链查询的时候,把操作链 split 出来,在平衡树上打标记/询问即可。这里要注意的是 splay push_up 操作的时候,必须维护正序及倒序的子树矩阵乘积,且严格按照中序遍历的方法乘。这是因为 LCT 操作的时候需要换根,换根就需要子树翻转。然而和上文
5. 总结
所谓动态 dp,就是把状态转移方程改写为矩阵后用数据结构维护的算法。通常配合线段树/平衡树/分块等维护序列的数据结构使用对于树上的链修改和链查询,可以使用树链剖分。
对于树上的子树查询,需要其它手段,敬请期待我的下一篇文章。
我是yyc樱初音(洛谷:樱初音斗橡皮),是一个初三OIer&MOer,想看更多内容请点赞、收藏,关注我吧!