2019暑期集训 - Day 3
惨烈啊,真的几乎啥都听不懂...
DP的优化
前缀和优化
当DP过程中需要反复从一个求和式转移的话,可以先把它预处理一下。运算一般都要满足可减性。
单调队列优化
前置技能:单调队列(经典的问题模型:洛谷P1886 滑动窗口)
用于优化形如 \(f_i=\min/\max_{j=l_i}^{i-1}\{g_j\}+w_i\),且满足 \(l_i\le l_{i+1}\) 的转移。
人话:对于序列中的每个点,从其左侧一段决策区间内的最值进行转移,且决策区间随着序列下标的增大也不断右移(就像窗口向右滑动)。
设 \(j<k\) ,容易发现如果 \(g_j\) 劣于 \(g_k\) 的话,那么当决策区间移动到 \(k\) 以后, \(j\) 永远不会成为最优决策点,再也不会被转移了。
于是,我们只要维护一个队列,满足下标递增,决策性递减。我们需要当前的队首成为最优决策点,那么当队首第一次超出了区间范围(以后也就永远超出了)就把它出队。为了保证单调性,队尾新加入点之前,要先把队列中比它劣的点依次从队尾出队。
决策单调性
一般分两类。无论是哪一类都需要细心发现决策之间的递变规律。
一种是用于优化形如 \(f_{i}=\min/\max (w_{i,j})\) , \(j\) 且对于每一个 \(i\) 和它的最优决策点 \(j\) 都有单调性的方程。
这样的方程对题目的性质要求较高(因为 \(j\) 是独立的)。
只需要维护指针,按照单调性不断寻找最优答案即可。
另一种是形如 \(f_i=\min/\max_{j=1}^{i-1} g_j+w_{i,j}\) ,且记 \(f_i\) 的最优决策点为 \(p_i\)(也就是 \(f_i\) 从 \(g_{p_i}+w_{i,p_i}\) 处转移最优)若满足 \(pi≤pi+1\) ,则该方程满足决策单调性。
在诗人小G的题解中蒟蒻用数形结合的思想讨论了一种可能可以快速准确地判断一个转移方程是否满足决策单调性的方法。
因为 \(j\) 不独立了,所以我们只能把有用的决策先存起来。
策略1——二分栈
我们使用决策二分栈(一种单调栈)来维护所有有用的决策,其中栈顶是当前最优决策。
为什么叫二分栈呢?
我们可以把 \(g_j+w_{i,j}\) 视为关于 \(j\) 的函数。因为决策单调,所以对于栈中的任意相邻两个决策点,我们都可以通过二分找到一个临界值 \(k\) ,使得序列中在 \(k\) 之前的时候,其中一个作为决策转移到 \(f_k\) 更优,而 \(k\) 以后另一个更优。可以借助函数图像来理解这个过程。
我们需要栈顶为当前的最优解。而如果栈中有不止一个元素,则可能存在一个 \(i\) ,使得到 \(i\) 之后栈里面的决策比栈顶优了。这个时候,如何快速判断并弹掉栈顶呢?
上面提到的这个可二分的性质就派上用场了。对于当前的 \(i\) ,如果当前栈顶下面与栈顶相邻的决策在 \(i\) 之前就比栈顶更优了,就要把栈顶弹掉。
这是决策单调性的基本思想,具体的题目实现起来也不一样。诸如有的题为了扩展功能,还需要把单调栈换成单调队列,等等。
策略2——分治
然而二分栈有一个局限性,那就是必须能快速计算 \(w_{i,j}\) 。如果不能 \(O(1)\) 算的话,在求临界值 \(k\) 的时候复杂度会严重退化。
既然转移过程是单调并且离线的,我们考虑分治。假设当前我们求解一段区间 \(f_{l,r}\) ,而所有 \(f_{l,r}\) 的最优决策点在 \([L,R]\) 之间。对于 \([l,r]\) 的中点 \(mid\) ,我们可以暴力扫一遍 \(L - mid\) ,找到它的最优决策点 \(k\) 。因为决策单调,所以 \(f_{l,mid-1}\) 的决策落在 \([L,k]\) 上,而 \(f_{mid+1,r}\) 的决策落在 \([k,R]\) 上,变成了两个规模减半的小问题。
套用分治的复杂度分析,总的时间也是 \(n \log n\) 的。
斜率优化
与决策单调性有着说不清的联系。
仍然是转移方程形如 \(f_i=\min_{j=1}^{i-1}g_j+w_{i,j}\)( \(max\) 同理)
考虑两个决策 \(j_1\) , \(j_2\) ,如果 \(j_1\) 比 \(j_2\) 优,那么 \(g_{j_1}+w_{i,j_1}\le g_{j_2}+w_{i,j_2}\) 。
这时候根据题目特点把w展开,如果式子能化成 \(\frac{y_{j_1}-y_{j_2}}{x_{j_1}-x_{j_2}}\le k_i\) 的形式,那么我们把每个决策看成点 \((x_j,y_j)\) 分布在坐标系上,而真正有用的决策点实际上形成了一个凸壳。(由类似线性规划的寻找最优解的过程可以发现)
于是我们用数据结构维护凸壳上的所有点,具体实现依题而定。
不管是什么题,加入决策点的时候要保证斜率递增/递减。
如果 \(x\) 单调,可以用单调栈维护凸壳,转移时使用当前直线的斜率(线性规划),在栈内二分最优解。
如果斜率k单调,那么用单调队列维护凸壳,队首为当前最优决策。转移之前如果队首不优就出队。
这篇博客留给我以后仔细学习
摘自 大佬的总结

浙公网安备 33010602011771号