【题解】P8885 dp套dp 矩阵乘法优化dp 分治
感觉全是套路的一个题,但很有意思,适合当 dp 套 dp 思想的入门理解。模拟赛赛时搬了个卡常版本被卡成了 ,很神笔,感觉这题难度不在数据结构部分吧。
定义一个串是奇的当且仅当这个串有奇数个本质不同可空子序列。
定义一个串是好当当且仅当这个串有奇数个奇子串。
本质不同子序列计数(奇串的判定)
广为人知。
考虑求出一个固定位置
维护 表示前 个位置,目前以 结尾的子序列计数。
转移方式:
即对于所有原本的子序列(包含空串),新加入一个数都可以使得它变成一个以这个数结尾的子序列,而对于其他串,并不以它结尾的子序列的个数。
把 数组拍平(滚动数组)。我们只需要记录每个位置的奇偶性。
考虑和为 的时候会将 赋值为 ,而此时和为 的时候会将 赋值为 ,所以 数组中无论如何是只会有不超过 个 的,考虑记录一个 ,表示 ,即该串的本质不同子序列数,那么在数组后面放一个 ,相当于交换 和 的奇值,过程如下。
所以我们只需要记录目前哪个位置的 有值即可递推信息并维护本质不同子序列数。
奇子串计数(好串的判定)
因为 dp 统计的是一个串的 dp 数,现在统计的就是所有子串的 数组的信息,又因为我们要一位一位来,所以 考虑 dp 套 dp:统计目前 数组的 dp 值对应的子串数。
令 表示目前这个串的 dp数组为 的子串个数,其中 被简化为 表示有值位置。
统计所有子串的方式即是:对于每个位置:
- 加入一个以这个位置开头的空串。
- 进行转移—— 原本的 添加了末尾的数会转移到 ,那么就将 转移到 。
- 统计所有以此位置结尾的奇串个数,累加至 中。
因为 只有 个有意义的值,所以 存三个状态。
我们可以统计对整个子串进行上面这个过程,通过 的奇偶性来判断这个串是否为好串。
填充方案数计数(将好串的判定放入计数中)
我们已经能够统计这个子串是否是好的,只需要维护 和 的值,接下来我们要维护所有填充方案的好串的判定。
考虑再套一层 dp:统计目前 数组与 对应的的填充方案。 表示目前填充方案使得目前的共统计状态为 和 ,因为本质不同的 与 只有 种,所以 的状态只有 种。
在确定了这个状态后某个 会转移至 那么在进行转移时将 转移至 即可。
直接进行转移,时间复杂度 ,其中 。
数据结构优化
观察到这个区间查询的 dp 的形式是一个线性的转移,且第二维较小,考虑使用矩阵乘法优化转移,使用数据结构区间查询某段矩阵的乘积,时间复杂度可以做到 ,很基础不在赘述。
常数优化与状态减少
首先线段树查询不需要整个矩阵的信息,故将矩阵乘矩阵优化成向量乘矩阵,复杂度 ,可以过掉这个题了。
首先这个矩阵的大小是很大的,但是单个位置的转移矩阵较为稀疏只有 个值,这样的矩阵的乘法是可以 完成的,所以我们不想像线段树那样合并两个一般矩阵,考虑猫树分治求答案,这样维护前后缀乘积的答案就是一般矩阵乘上单个转移矩阵,复杂度 。
在复杂度上我们很难再优化了,但是状态数还可以再减少!观察到我们前面处理 的时候加入的串的总数的奇偶性是固定的(给 改变了一次奇偶性,所以我们是可以通过目前的总长度和 推出来 ,故可以将需要记录的的状态减少 个,可以将 减小到 ,但是需要分别记录奇数和偶数的矩阵,将一个常数 从平方内移到了平方外,会快很多。
笔者没加最后这个优化,目前最优解第二,这里是代码。
本文已经结束了。本文作者:ღꦿ࿐(DeepSea),转载请注明原文链接:https://www.cnblogs.com/Dreamerkk/p/17970907,谢谢你的阅读或转载!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步