线段树优化
概述
-
线段树优化通过线段树能快速区间取值和区间修改的特性,高效实现具有连续性的 dp 转移。
-
所谓的连续性可以是顺推的转移目标点为连续区间,也可以是逆推的转移出发点为连续区间,这一连续不一定必须是原数组下标这一维度上的。
-
效果一般为将 KD1D 的 dp 的 \(O(n)\) 的转移优化至 \(O(\log)\),转移大于 1D 的 dp 中线段树优化较为罕见。
-
应当指出的是,有些时候要刻意使用同构 dp 来方便进行线段树优化,譬如一个典型的手法是将“考虑了前 \(i\) 个,最后一个选的是 \(j\)”改成“考虑了前 \(i\) 个,最后一个选的是 \(i\)”,将状态的复杂度转嫁到转移上,然后使用线段树优化。
例题
CF1788E Sum Over Zero
-
题意略。赛时我可能智商欠费。
-
容易看出一个线段 \([l,r]\) 合法当且仅当 \(ps_r-ps_{l-1}\geqslant 0\)。可以转换成 \(ps_r\geqslant ps_{l-1}\),这里 \(ps\) 是 perfix sum 的缩写。
-
故考虑设计如下 dp:
-
状态设计:\(f_i\) 表示考虑了前 \(i\) 个点,当前最大合法总长度是多少。
-
初始化:\(f_0=0\)。
-
状态转移方程:\(f_i=\max(f_{i-1},\max_{j=0}^{i-1} (\max_{k=1}^j f_k+(i-j)))\),其中后一个转移需要满足 \(ps_i\geqslant ps_j\)。
-
不妨记 \(g_i=\max_{j=1}^i f_i\),显然可以预处理,稍微化一下式子(只考虑后一种转移),得到 \(\max_{j=0}^{i-1} (g_j-j)+i\)。事实上因为前一种转移,\(g\) 本身就具有 \(g\) 的性质,不过这里我们假装它没有。
-
注意到我们的转移顺序天然地保证了 \(j<i\),故我们可以开一棵值域线段树,树上第 \(i\) 个叶节点表示 \(\max_{ps_j=i} (g_j-j)\)。在得到 \(g_x\) 后将 \(g_x-x\) 扔到树上的第 \(ps_x\) 个叶节点上,转移的时候只要做前缀询问就好了。
-
那其实应该也可以是树状数组,单点修改,前缀最大值...哦值域太大了啊。那么考虑将这一转移对偶,即我们就开关于下标的树,此时转移顺序变成按 \(ps\) 从小到大了。哦错了,这会使得前一种转移不可行,于是炸了。
-
-
复杂度 \(O(n\log)\)。