像潮落潮涌,送我奔向自由。|

寂静的海底

园龄:3年2个月粉丝:59关注:15

【题解】P8885 dp套dp 矩阵乘法优化dp 分治

感觉全是套路的一个题,但很有意思,适合当 dp 套 dp 思想的入门理解。模拟赛赛时搬了个卡常版本被卡成了 60,很神笔,感觉这题难度不在数据结构部分吧。

定义一个串是的当且仅当这个串有奇数个本质不同可空子序列。

定义一个串是当当且仅当这个串有奇数个子串。


Part 1 本质不同子序列计数(奇串的判定)

广为人知。

考虑求出一个固定位置

维护 fi,c 表示前 i 个位置,目前以 c 结尾的子序列计数。

转移方式:

fi,ai=fi1,j+1fi,j=fi1,j(jai)

即对于所有原本的子序列(包含空串),新加入一个数都可以使得它变成一个以这个数结尾的子序列,而对于其他串,并不以它结尾的子序列的个数。

f 数组拍平(滚动数组)。我们只需要记录每个位置的奇偶性。

考虑和为 0 的时候会将 fai 赋值为 1,而此时和为 1 的时候会将 fai 赋值为 0,所以 f 数组中无论如何是只会有不超过 11 的,考虑记录一个 fU,表示 f0+f1+1,即该串的本质不同子序列数,那么在数组后面放一个 ai,相当于交换 fUfai 的奇值,过程如下。

fai=fj=fUfU=fU+faifai=2fUfai=fai

所以我们只需要记录目前哪个位置的 f 有值即可递推信息并维护本质不同子序列数。


Part 2 奇子串计数(好串的判定)

因为 f dp 统计的是一个串的 dp 数,现在统计的就是所有子串的 f 数组的信息,又因为我们要一位一位来,所以 考虑 dp 套 dp:统计目前 f 数组的 dp 值对应的子串数。

gof  表示目前这个串的 f dp数组为 of 的子串个数,其中 of 被简化为 {0,1,U} 表示有值位置。

统计所有子串的方式即是:对于每个位置:

  1. 加入一个以这个位置开头的空串。
  2. 进行转移—— 原本的 f 添加了末尾的数会转移到 f,那么就将 gf 转移到 gf
  3. 统计所有以此位置结尾的奇串个数,累加至 tot 中。

因为 f 只有 3 个有意义的值,所以 gf 存三个状态。

我们可以统计对整个子串进行上面这个过程,通过 tot 的奇偶性来判断这个串是否为好串。


Part 3 填充方案数计数(将好串的判定放入计数中)

我们已经能够统计这个子串是否是好的,只需要维护 g0/1/Utot 的值,接下来我们要维护所有填充方案的好串的判定。

考虑再套一层 dp:统计目前 g 数组与 tot 对应的的填充方案。 h(g,tot) 表示目前填充方案使得目前的共统计状态为 gtot,因为本质不同的 gftot 只有 24 种,所以 h 的状态只有 16 种。

在确定了这个状态后某个 (g,tot) 会转移至 (g,tot) 那么在进行转移时将 h(g,tot) 转移至 h(g,tot) 即可。

直接进行转移,时间复杂度 O(qn×s),其中 s=16


Part 4 数据结构优化

观察到这个区间查询的 dp 的形式是一个线性的转移,且第二维较小,考虑使用矩阵乘法优化转移,使用数据结构区间查询某段矩阵的乘积,时间复杂度可以做到 O((n+qlogn)s3),很基础不在赘述。


Part 5 常数优化与状态减少

首先线段树查询不需要整个矩阵的信息,故将矩阵乘矩阵优化成向量乘矩阵,复杂度 O(ns3+qs2logn),可以过掉这个题了。

首先这个矩阵的大小是很大的,但是单个位置的转移矩阵较为稀疏只有 O(s) 个值,这样的矩阵的乘法是可以 O(s2) 完成的,所以我们不想像线段树那样合并两个一般矩阵,考虑猫树分治求答案,这样维护前后缀乘积的答案就是一般矩阵乘上单个转移矩阵,复杂度 O(nlogns2+qs2)

在复杂度上我们很难再优化了,但是状态数还可以再减少!观察到我们前面处理 g 的时候加入的串的总数的奇偶性是固定的(给 gU 改变了一次奇偶性,所以我们是可以通过目前的总长度和 g0,g1 推出来 gU,故可以将需要记录的的状态减少 3 个,可以将 s 减小到 8,但是需要分别记录奇数和偶数的矩阵,将一个常数 2 从平方内移到了平方外,会快很多。

笔者没加最后这个优化,目前最优解第二,这里是代码

posted @   寂静的海底  阅读(36)  评论(0编辑  收藏  举报  
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起