动态规划及优化
决策单调性
定义:
单调矩阵:每行最值位置单调不降。
完全单调矩阵:每个子矩阵都是单调矩阵,这里的子矩阵可以不连续。
蒙日矩阵:满足四边形不等式的矩阵,蒙日矩阵一定是完全单调矩阵。
一般的决策单调性优化dp可以看作在蒙日矩阵一列加上一个数,求一行最值的过程,用数据结构优化。
一般矩阵的\((\min,+)\)卷积只能\(O(n^3)\),蒙日矩阵的\((\min,+)\)卷积可以决策单调性做到\(O(n^2)\),并且结果还是蒙日矩阵。
常见的方法有分治,二分栈和SMAWK。
「雅礼集训 2017 Day5」珠宝
观察到\(c_i\)很小,所以直接按照\(c_i\)分组,然后每组内会选最大的一段前缀,这个东西显然满足决策单调性,所以可以按\(\mod c_i\)分组,然后分治即可。
【UNR #5】航天飞机调度
牛逼题。
首先有\(O(q^2)\)的dp:设\(f_{i,j}\)表示到了第\(i\)个事件,某个在\(p_i\),另一个在\(j\)的最大值,注意到\(p_i\)只有\(O(q)\)个取值,所以时间复杂度和询问都是\(O(q^2)\)的。
先考虑特殊性质\(B\),因为这是一个三角剖分上的最短路,所以具有三角不等式,即\(dis(a,b)+dis(b,c)\geq dis(a,c)\),所以我们要尽量让它绕路。如果存在\(a,b,c,d\),一个人从\(a\)走到\(d\),另一个从\(b\)走到\(c\),肯定不优于一个人从\(a\)走到\(c\),另一个人从\(b\)走到\(d\),因为三角剖分是一个平面图,因此\(a\to c\)和\(b\to d\)的路径一定有一个交点,而\(a\to d\)和\(b\to c\)的路径权值和加起来肯定小于等于走到那个交点,因此这个性质是成立的。
那就好办了啊,\(B\)一定是先两个人交替走,然后一个人一起走,所以某个点只需要知道和前两个点的权值就好了,是\(2q-1\)次询问。
再考虑一般的情况,这个东西就是四边形不等式在\(u\leq v\leq l\leq r\)的情况,但是我们实际上需要\(u\leq v,l\leq r\),不能直接决策单调性。
最神奇的一步就是这里:我们将每个点拆成\(i\)和\(i+n\)两个点,并且让矩阵里只有\([i,i+n)\)范围内的点有权值,否则为\(\infty\),则就满足了决策单调性,并且答案不变。
现在的问题就是每次在一行加上一个非负值,查询一列的权值,一般的dp加上行和查询列的权值单调所以可以二分栈,这里不太行。我们考虑维护每一行作为最大值出现的区间,那么加上一个值之后区间会变大,变大多少可以二分,用线段树维护,询问次数\(2q\log n+2q\),时间复杂度\(O(q\log ^2n)\),可以做到\(O(q\log n)\)但是我懒。
「KDOI-03」序列变换
根据NOIP2021T3,首先前缀和,发现每次就是交换相邻两个数,然后求相邻两项不同的值不超过\(k\)的最小操作次数。
设\(q_{l-1}=0\),对于\(k\)是偶数的情况,相当于\(1\)的连续段不超过\(k/2\)段,\(k\)是奇数相当于不超过\(k/2+1\)段,如果为\(k/2+1\)段那么最后的一个数要是\(1\)。
设\(F_{l-1,r}\)表示\([l,r]\)区间的\(1\)放到一起的最小代价,\(G_{l-1,r}\)表示放到最后的最小代价,则\(k\)是偶数的时候答案矩阵为\(F^{k/2}\),否则为\(F^{k/2}G\),则快速幂可以做到\(O(n^3\log k)\)。注意到是蒙日矩阵,因此可以做到\(O(n^2\log k+q)\),可以通过。
凸优化
用于每层\(dp\)是凸壳的情况,假设是下凸壳。
如果要求某个位置\(k\)的具体的值,可以二分一条斜线的斜率去截这个凸壳,算截距最小值对应的凸壳上在哪个点,和\(k\)比较得出斜率应该哦更大还是更小。
如果要求整个数组,并且形如\(f_{i,j}=\min(f_{i-1,j},f_{i-1,j-k}+w_k)\)只有相邻项之间才有转移的时候可以用两个堆维护凸壳,一个维护斜率为负的地方的斜率转折点,一个维护正的,俗称slope trick。发现这样只知道差分值不知道具体值,可以构造方案,也可以求某个比较好求的点的值。
「Wdoi-2」禁断之门对面,是此世还是彼世
首先这个矩阵是诈骗你的,直接将一行的最优方案重复\(n\)次即可,则可以求出一行的最优\(b\)和然后乘上所有\(a\)的和。
考虑这个东西怎么求,发现就是求一个最小权,流量为\(t\)的匹配,并且\(i\)不能和\(i\)匹配。
首先我们说明存在最优方案没有匹配\((a,c),(b,d)\)形如\(a\leq b\leq c\leq d\),因为显然可以让\((a,b)\)匹配,\((c,d)\)匹配,不会更劣。
再因为\((i,j)\)匹配相当于\((j,i)\)匹配,因此可以将\(i\geq j\)的情况归约到\(i\leq j\)的情况,除非有的点已经被占了。
强制第一个匹配的点是向后匹配的,则显然只会向后匹配一个,第二个点如果向后匹配也是如此,则答案的形式就是\((i,i+1),(i+1,i+2),\dots,(j-1,j),(j,i)\)。其中\((j,i)\)可以有也可以没有。
同时可以证明\(|i-j|\geq 4\)的时候是不优的,具体可以拆成两个\(\geq2\)的,则端点可以少两个。
于是可以得出一个基本的dp模型:设\(dp_{i,j}\)表示到了\(i\),选了\(j\)个,则可以枚举转移,复杂度\(O(n^2)\)或者\(O(n^3)\)。
众所周知二分图匹配可以转化成网络流,而网络流是上凸壳,因此直接上wqs二分优化到\(O(n\log W)\)。
序列变换
这个题相当于每次给出\(x\),操作\(dp_i=\min\limits_{j=i-B}^{i-A}{dp_j}\),然后给\(dp_i\)加\(|x-i|\)。
发现初始是个凸壳,经过两个操作都是凸壳,所以归纳任何时刻\(dp\)都是个下凸壳。
第一个操作相当于给左凸壳所有拐点加上\(A\),给右凸壳所有拐点加上\(B\),第二个操作相当于加入两个为\(x\)的拐点,经过每个拐点斜率增加\(1\),堆维护斜率为\(0\)的一段左右两端点。
这里讲一下怎么输出答案,朴素的方法就是每个点代\(A_i=(i-1)A+1\),然后求最值。但是实际上可以构造方案。首先每个位置有个限制\((i-1)A+1\),然后求出每个时刻的最值点\(p_i\),这样的话每个时刻的最值就会收到下一个时刻最值的限制,就可以求出合法的最值点了。
Hunting Monsters
首先这个打怪的套路貌似在CTT考过。先将怪分成两种:打了回血的和打了掉血的。打了回血的显然先打,而且先打掉血少的再打掉血多的最优。打了掉血的直接贪心貌似有些困难,但是实际上可以从最终的状态反过来考虑,也是一个回血的过程。因此先打回血多的,再打回血少的最优。如果我们将这个序列排序,则最终的打怪序列就是这个的一个子序列。
显然设\(f_{i,j}\)表示打到第\(i\)个怪,打了\(j\)个的最优值。但是你发现这个最优值的比较是困难的,也就是说我们没有办法比较\((a,b)\)这样的二元组,其中\(a\)是最多掉的血,\(b\)是相比于最多掉的血的时候,会回多少血。所以这里要倒着dp,这样有一个好处,就是不用管回多少血,这对之前打的没有影响。因此\(f_{i,j}=\min(f_{i+1,j},\max(f_{i+1,j-1}-B_i,0)+A_i)\)。
掉血的怪\(B_i\)是递增的,这是好的,也就是说如果一个位置小于\(B_i\),那么这个位置就不会动了。那么就是考虑\(f_{i+1,j-1}\geq B_i\)的情况,差分之后就相当于插入一个\(A_i-B_i\),可以用堆维护。
回血的怪显然打一个前缀,则和掉血的怪可以双指针合并,总时间复杂度\(O(\sum n\log n)\)。