线段树优化 DP
线段树优化转移
有时,DP 的转移区间不具有单调性,或者历史 DP 值对现在的 DP值的贡献发生的变化较大,无法使用单调队列分离无关项,就可以使用线段树优化。
当一些产生贡献的项可以被刻画为区间修改时,则可以使用线段树优化。
例题
P9871 [NOIP2023] 天天爱打卡
请参考典题题解。
P2605 [ZJOI2010] 基站选址
和 天天爱打卡 很相似,只是需要给 dp 数组多加一个维度 \(k\)。在本题中必须要把 \(k\) 放到外层循环,才能在线段树上把这一维压掉。否则就需要开 \(k\) 棵线段树,会 MLE。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
|
线段树维护矩阵乘法
如果 DP 的转移过程可以被刻画为(普通 / 广义)矩阵乘法,得益于矩阵乘法具有结合律,可以使用线段树维护区间矩阵乘法的结果,从而做到 \(O(\log n)\) 查询区间答案。
例题
CF750E New Year and Old Subsequence
题目大意
定义一个数字串满足性质 nice
当且仅当:该串包含子序列 \(2017\),且不包含子序列 \(2016\)。
定义一个数字串的 ugliness
为:该串至少删去几个字符,可以使得剩余串满足性质 nice
;如果该串没有满足性质 nice
的子序列,则该串的 ugliness
是 -1
。
给定一个长度为 \(n\) 的数字串 \(t\),和 \(q\) 次询问,每次询问给定一个区间 \([l,r]\),你需要回答 ugliness(t[l,r])
。
\(1\le n,q\le 2\times 10^5\)
考虑一个朴素的 DP。设 \(f_{0/1/2/3/4}\) 表示已经匹配出了 \(\emptyset/2/20/201/2017\),且不包含 \(2016\),至少需要删去几个字符。朴素的转移:
我们希望求出区间的答案。因此考虑将转移刻画为矩阵乘法,然后使用线段树解决。我们记状态矩阵
容易写出转移矩阵:
注意,因为转移的过程主要使用加法和 \(\min\),因此此处的矩阵乘法是指 加法-\(\min\) 的广义矩阵乘法。空白部分均为 \(+\infty\)。
我们预处理出数字串每个位置所对应的矩阵,建立一棵线段树维护区间矩阵乘法的结果:
Matrix 结构体
建树
建树时间复杂度 \(O(5^3n)\)。
查询时,我们不必让线段树返回整个区间对应的转移矩阵。我们可以传入一个初始的状态矩阵:
然后让线段树把每段对应的转移矩阵 \(op_i\) 按顺序乘到 \(res\) 上(\(res=res\times op_i\)),最后返回 \(res\)。这样做可以避免两个 \(5\times 5\) 的矩阵直接相乘,而是让 \(1\times 5\) 和 \(5\times 5\) 的矩阵相乘,从而将单次查询的时间复杂度降低到 \(O(5^2\log n)\)。
查询
查询操作的技巧
维护矩阵乘法时,查询操作不返回一个完整的转移矩阵,而是返回一个一维的向量,这时一种很常见的优化。有些场景的时间复杂度高度依赖于这种优化。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
|