DP优化

朴素DP都写不出来怎么办捏qwq

纵观各种优化方式,可以发现DP的优化在于两个方面:转移和状态数。

转移的优化很多,大多数的优化都关注于转移。而状态数的优化大多是针对状态的设计。

一类特殊的DP:整体DP,试图兼顾这两个方面,利用DS囊括各种DP的状态时,同样利用DS的结构将具有相同转移的状态一起转移。

矩阵快速幂优化

将转移写成(max,+)矩阵乘法的形式,然后上矩阵乘法,可将O(n)的转移优化到O(logn)

矩阵的构造函数一定要写啊啊啊啊啊。小心出现神秘错误。

前缀和优化

一般的DP加上前缀和可以优化形如fi=j=lrg(j)+C的式子。

对于一段连续区间求和,且g(j)是一坨只关于j的式子(如fj,fj+wj)加上常数(关于i的式子或者真的常数)就可以使用。

特别的,如果区间的一端是固定的,只增不减,用变量存一下就好了。

单调队列优化

优化形如fi=min/maxj=lrg(j)+C的式子,其中要保证随着i增大,l,r单调不减。

g(j)是一坨只与j相关的式子,C同上文。

本质是及时排除不可能再有用的决策。

单调栈与单调队列都是类似的。

特别的,如果区间一端固定,另一端只增不减,用变量存一下就好了,不必用单调队列。

数据结构优化

特征:

一个状态可以从某个区间中的每个状态转移过来,且随着状态改变,对应区间没有单调性。

Sol:

用数据结构维护一下决策即可。

其实单调队列优化也算是特殊的DS优化,但是对应区间有单调性的性质很好,所以跑得更快。

线段树优化DP

特点是发现一次转移对应在DP数组上的区间修改和单点修改。于是用线段树维护DP数组。

[ARC073F] Many Moves

平衡树优化DP

其实很多时候用的是unordered_map或者map,把DP数组的一维压到这里面,并且可以快速查找可以转移的位置。

李超线段树优化DP

形如fi=minj{kx+b}+C一类的柿子,其中xi相关,kbj相关。这样可以视作插入了一堆线段或者直线(这个区别在于一个位置能对哪些位置做出贡献),查询一个位置处一次函数最值。可以简单用李超线段树维护。

如果维护一堆直线就是单log,维护线段是双log

维护线段的情况中如果线段有凹凸性就可以斜率优化。

整体DP

其实和数据结构优化DP有类似之处。

但是整体DP直接将DP中的一维压入数据结构之中。

通过数据结构上的单点修改/区间修改来快速维护此时此刻(枚举到的其他维度)的各个位置的DP值。

感觉像是将转移方程中的运算直接搞成修改。

可以发现整体DP是对O(1)转移的DP的再优化(因为状态数太多了),用数据结构就可以在状态数很多的情况下也能够实现快速转移(比如线段树中通过标记把一堆状态一起改)。

做的时候还是要先写朴素DP,然后通过一些手段优化到O(1)转移,然后上数据结构对状态数很多的情况进行优化。

线段树合并

维护树上的整体DP很方便(因为合并)。

普通的序列上的线段树不太方便维护树上的信息,于是改造成线段树合并。

维护时同样支持单点修改/区间修改 etc.

P5298 [PKUWC2018] Minimax

首先是朴素DP。

定义fu,i表示结点u最后权值为i的概率(对应题中的Di)。观察到题目给的是二叉树,然后开始想朴素转移:

fu,i=(1pu)(fls,ij>ifrs,j+frs,ij>ifls,j)+pu(fls,ij<ifrs,j+frs,ij<ifls,j)=fls,i[(1pu)j>ifrs,j+puj<ifrs,j]+frs,i[(1pu)j>ifls,j+puj<ifls,j]

方程里的前缀后缀和直接优化就到O(1)转移了。

但是由于值域很大,状态数超级多,O(1)转移也无法接受。于是上线段树合并,同时维护前缀和与后缀和以及乘法标记。

具体而言,点u上的线段树维护着fu,1V。现在考虑如何通过线段树合并实现上面的转移。

首先,当点u没有儿子,是叶子时,将其权值插入到它的线段树中,概率设为1。当点u只有一个儿子时,直接继承儿子的线段树树根。当点u有两个儿子时,合并两个儿子,将合并后的树根给u

合并中,改造一下原来的merge,记录当前合并的线段树节点lsu,rsu,当前合并的区间[l,r],区间[1,l1]lsrs的前缀和,[r+1,V]lsrs的后缀和,以及pu

lsu为空时,方程中左边那一坨都为0,右边frs,i乘上的一坨为定值,所以直接区间乘打上标记。当rsu为空时同理。

由于题目中给了权值互不相同,所以两棵线段树合并时值域不会重叠,于是肯定会出现一棵为空而另一棵不会空的情况。所以就做完了。

决策单调性优化

四边形不等式

就是利用四边形不等式判断决策是否有单调性。

证明不会。

单调性猜测一下就好,考场上证不了一点,可以打表找找规律看是否单调。

有了决策单调性就好办了,主要有两种方式来优化DP。

单调队列+二分

我们把决策塞到单调队列里面(其实很自然,决策有单调性嘛),然后维护一下单调性即可。

让我们更具体一点。

队列中保存决策d(l,r,p),表示区间[l,r]的当前最优决策点为p

  1. 首先队列中塞入(1,n,0)(或者其他初始条件),准备开始DP。

  2. 现在我们要计算dpi,先从队首将r<i的决策出队(因为已经没用了),然后用队首计算dpi

  3. 然后考虑插入i这个决策。不断取出队尾(l,r,p),若用i更新l更优,那么队尾的这个决策就是没用的,出队。

  4. 最后对于队尾(l,r,p),用p更新l更优,那么i这个决策可能没用,也可能在lr之间某处开始比p更优。于是二分,注意边界要开大来判断无解(就是i没用的情况)。

  5. i没用,就跳过了去算下一个。否则要插入i这个决策。设二分出ip更优的第一个位置为u,修改(l,r,p)(l,u1,p),插入决策(u,n,i)

分治

是离线版本的决策单调性优化,即当前的DP数组从另一个现在完全已知的数组中转移过来。也就是说从fj转移到fi的转移方式不可以用分治。

但是分治真的很好写。

设当前要确定决策点的区间为[l,r],其对应的决策点所在区间为[dl,dr]

那么我们可以先算mid=l+r2,暴力扫一遍[dl,dr]确定mid的决策点dm,然后向下分治:

[l,mid1]对应的决策点区间为[dl,dm][mid+1,r]对应的决策点区间为[dm,dr]。(含有dm是因为不一定严格单调)

然后就做完了。

指针移动的Trick

有些w(l,r)不好O(1)求,只能O(n)用指针扫。

但是每一层都暴力扫会假,不会证。

于是可以在分治外维护指针,每一层要算贡献时就像莫队一样移动指针即可。注意这样每层的指针移动量是O(n)的,所以总的还是O(nlogn)

注意事项

  1. 带有上取整,下取整,绝对值的大概率不满足决策单调性,要转化(绝对值就拆掉,取整就先用long double算,最后再取整)。

  2. 可以猜单调性但别乱猜,最好能打表,时间充裕再证。

凸优化

斜率优化DP

形如fi=min{fj+C(j)K(i)A(j)}+B(i)

其中K(i),C(j),B(i),A(j)是关于i,j的一次项。

变形为fiB(i)=min{fj+C(j)K(i)A(j)}

令:y=fj+C(j),x=A(j),k=K(i,j),b=fiB(i),那么原式就是b=ykx。然后对于一个i,就是用固定的k移动一条直线去截平面上已有的点,使得截距最小。

注意到有用的点都是凸壳上的点(上凸或者下凸)。

单调队列维护凸包

和普通的单调队列一样。以下凸为例,弹出队尾当且仅当kqt,qt1>kqt,x

注意:如果要二分,上面的>应为,是为保证斜率的严格单调。

横坐标单调,斜率单调

那就是板子啊,随便单调队列就行。

注意横坐标不严格单调时可能相等的情况,算斜率的时候判一下。

横坐标单调,斜率不单调

还是单调队列维护点,然后二分查询。

横坐标不单调,斜率单调

没做过,或许继续单调队列,然后在队列中二分插入点。

横坐标不单调,斜率不单调

上李超线段树/CDQ分治/平衡树。

wqs二分

Slope Trick

posted @   RandomShuffle  阅读(27)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示