杭电春季赛5 1004(矩阵乘法优化dp)
1004
考虑缩小问题规模:
首先,可以将 1~n 序列内的所有数都模 \(m\),这样值域就缩减为了 \([0, m - 1]\)。
其次,对于模 \(m\) 的某个剩余类内的所有数,选其中的 \(i\),\(m + i\),\(2m + i\)... \(km + i\) 个数并无差别。这样 \([0,m - 1]\) 内每个数的个数也可以缩减到 \(<= m\) 的范围内来考虑。
至此,所有相关的数据范围都被缩减到了 \(m\) 以内。可以考虑 \(dp\):
\(dp[i][j][k]\):考虑数字0~i,其中选 \(j\) 个数字 \(i\),选取的子集内所有数之和模 \(m\) 为 \(k\),方案数。
显然 \(ans = \sum_{j=0}^{m-1}dp[m - 1][j][0]\)。
\(trans\):
第二维可以利用前缀和优化 \(dp\) 加速。
转移复杂度看似也是 \(O(m^{3})\) 的,但实际上 \(d[i][j]\) 的计算也非常耗时。
(本篇重点)考虑如何计算 \(d[i][j]\):
观察 \(d[i][j]\) 的表达式,其中 \(num[i]\) 的范围特别大,不可能计算其中的每一项。可以利用 \(d[i][j]\) 的定义式来巧妙转换下思路:
\(d[i][j]\) 的另一种表述:有 \(num[i]\) 个数字,这些数字两两不同(在模 \(m\) 之前,它们是互不相同的),要求选择的数字数量 模\(m\) 后为 \(j\),方案数。
定义 \(f[i][j]\):考虑前 \(i\) 个数,选取它们的某个非空子集,数量 模\(m\) 后为 \(j\),方案数。
数组 \(f\) 的转移式可表示为:
特殊地,当 \(j = 0\) 时,有:
这里的 \(i\) 会达到 \(O(n)\) 级别,特别大,不可能直接递推。
考虑用矩阵来加速递推过程:定义矩阵 \(A\)(大小以 \(7 \times 7\) 为例,实际大小为 \(m \times m\)):
并设大小 \(1 \times m\) 的矩阵 \(B\) 为:
\(d[i]\) 实际上是一个 \(1 \times m\) 的矩阵。矩阵 \(A\) 实际上就是每一步转移时的因子(每一轮都固定,因此最终可以表示为 \(A\) 的次幂,可以用矩阵快速幂计算)。而矩阵 \(B\) 就是 \(f[0]\)中初始的每一项(只有 \(1\) 种方案:不选数字。模 \(m\) 结果默认为 \(0\))。
因此,\(d[i]\),\(A\),\(B\) 之间的转移式可写作:
其中 \(A^{num[i]}\) 这一项用矩阵快速幂来直接计算。
对于 \(i \in [0, m - 1]\) 中的每一项,都按上述方式计算,总复杂度是 \(O(m^{4}logn)\) 的(不是最优的复杂度,提交也会 \(TLE\),不过整个过程都是本蒟蒻自己摸索出来的,只是为了做一次记录。正解暂时还不想补qwq...)