1 条题解

  • 0
    @ 2025-8-24 22:43:25

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar ღꦿ࿐
    一直游到海水变蓝

    搬运于2025-08-24 22:43:25,当前版本为作者最后更新于2023-10-18 09:22:02,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


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

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

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


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

    广为人知。

    考虑求出一个固定位置

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

    转移方式:

    fi,ai=fi1,j+1f_{i,a_i}=\sum f_{i-1,j}+1 fi,j=fi1,j(jai)f_{i,j}=f_{i - 1,j} (j\neq a_i)

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

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

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

    fai=fj=fUf'_{a_i} = \sum f_{j} = f_U $$f'_{U} = f_{U} + f'_{a_i} - f_{a_i}=2f_U-f_{a_i}=f_{a_i} $$

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


    Part 2\text{Part 2} 奇子串计数(好串的判定)

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

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

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

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

    因为 ff 只有 33 个有意义的值,所以 gfg_{f} 存三个状态。

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


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

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

    考虑再套一层 dp:统计目前 gg 数组与 tottot 对应的的填充方案。 h(g,tot)h_{ (g,tot)} 表示目前填充方案使得目前的共统计状态为 ggtottot,因为本质不同的 gfg_{f}tottot 只有 242^4 种,所以 hh 的状态只有 1616 种。

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

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


    Part 4\text{Part 4} 数据结构优化

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


    Part 5\text{Part 5} 常数优化与状态减少

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

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

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

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

    • 1

    信息

    ID
    8156
    时间
    2000ms
    内存
    512MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者