背景:czy上课讲了新知识,从未见到过,总结一下。
所谓动态dp,是在动态规划的基础上,需要维护一些修改操作的算法。
这类题目分为如下三个步骤:(都是对于常系数齐次递推问题)
1先不考虑修改,不考虑区间,直接列出整个区间的dp方程。这个是基础,动态dp无论如何还是dp(这一步是一般是重点)
2.列出转移矩阵。由于有很多修改操作,我们将数据集中在一起处理,还可以利用矩阵结合律,并且区间比较好提取,(找一段矩阵就好了),修改也方便。
3.线段树维护矩阵。对于修改,我们就是在矩阵上进行修改,对于不同的题目,我们要用不同的修改方式,和记录手段。但是都是线段树一个节点维护的是这个区间内矩阵的信息。如矩阵乘积,矩阵和等等。线段树的区间优势,可以应对区间修改问题。
T1:HDU5068
这里,由于是单点修改,所以直接到叶子节点,修改后再pushup就可以了。
线段树维护区间内矩阵乘积。
T2:CF Sasha and Array
就是斐波那契数列。
这里的可以原因是:提出B,因为矩阵右分配律,再提出一个M^x,还是矩阵右分配律。注意这里M^x,B是不能交换顺序的。但是M^x放在求和的前边乘,还是后边乘是无所谓的。因为都是M
可以用左分配律,也可以用右分配律。
其实代码不难想。
1.laz标记应当建一个和t[4*N]一样的laz[4*N],这样,每个结构体只存一个矩阵a,不但节省空间,而且内置函数的矩阵乘法还方便,因为无论如何都转移到a矩阵,而不用考虑是a乘laz还是laz乘laz。
2.数组越界了,被卡了很长时间。开a[3][3]就可以,没有发现的原因是,c++本地编译不会RE,放到CF上就会出现奇怪答案,而且莫名有的地方数组内的值就变了,比如说突然都变成0
3.注释不要太多,以免掩盖正解,导致把laz 下放注释掉了。。。
T3:
本质不同:不一样。长度不同,或者长度一样对应位置数字不全一样。
注意是子序列不是子串
注意,为什么用f[i][0/1]?因为当最后一位不一样时,这两个子序列一定不一样,所以f[i-1][0]和f[i-1][1]中的每一个都是不一样的。
并且,这还跟原数组数值0/1挂钩,很好联系上了。
加的一个1是就取这一位,其实是之前每一个都多了一位,就没有了最初的长度为一的子序列。所以加上。
也就是说,区间矩阵乘积结果的矩阵可以直接进行翻转,先翻再乘,和先乘再翻没区别。
直接正常维护就好,加一个rev标记。