凸优化 DP:k-子段和 系列问题

给定长度 n 的序列 a1an

问题 1:给定固定的 k,选 k 个不相交的子段,和最大是多少。

问题 2:支持两种操作,全局加和查询区间最大子段和,允许离线。

问题 3:对 k=1n,求选 k 个不相交的子段,和最大分别是多少。

问题 4:询问 q 次,每次询问从一个区间选 k 个不相交的子段,和最大是多少。

问题 1

法一:使用 WQS 二分。dp[i] 表示前 i 个数任意分段,每多一段就多 x 的最大收益。dp[i]=max(dp[i1],maxj(dp[j]+s[i]s[j1])),拆一下式子即可。复杂度 O(nlogV)

法二:模拟费用流。线段树支持查询最大子段和即可。复杂度 O(klogn)

问题 2

本题即 P5073 世上最幸福的女孩

离线操作。把全局加 k 的操作删除,转而给第二种操作升维:(l,r,k) 表示询问 al+k,al+1+k,,ar+k 的最大子段和。

考虑用线段树处理这个问题。最大子段和总共需要四个信息:最大前缀和、最大后缀和、区间总和、最大子段和。

  1. 区间总和。这是好求的,对于参数 (l,r,k),其区间总和是 i=lrai+k=srsl1+k×(rl+1)

  2. 最大后缀和。这和最大前缀和类似。

  3. 最大前缀和。

    先考虑对一个结点怎么求。

    在结点 [l,r] 上,用 (i,fi) 表示 alr 有一个长度 i 的前缀,其和为 fi,则对于 (l,r,k),最大前缀和就是 max{ik+fi},容易发现这只会取到 (i,fi) 的上凸壳。如果能预先求出上凸壳,就可以二分求最大前缀了。

    怎么求上凸壳?先求左右儿子的上凸壳,给右边的凸壳向上平移左边的总和(这显然还是个凸壳),然后利用闵可夫斯基和线性合并。一共 O(logn) 层,每层都是线性合并,预处理凸壳复杂度 O(nlogn)

    现在知道怎么对于一个结点求它的最大前缀和了,多个结点时按顺序合并即可。

  4. 最大子段和。

    有了前后缀,最大子段和也很简单了。先递归左右儿子,先取左右儿子的最大子段和,然后用左儿子的最大后缀和右儿子的最大前缀拼起来。

把询问区间拆成 O(logn) 个线段树结点即可。

但如果这样子做是 O(nlog2n) 的,或许其他题可以过,但是 Ynoi 的题还是给予足够的尊重吧。我们要优化到 O(nlogn)。我们发现,如果把这些询问再离线一次,把所有询问按 k 排序,每个凸壳的最优决策点是单调向右的,用指针即可解决。复杂度 O(nlogn)

但是这样做还是通过不了本题。因为空间复杂度是 O(nlogn) 的。瓶颈在于需要把所有凸包在对应结点都存下来。如果能每次只存一层的凸包就好了。一个想法是把所有询问记录到每个对它生效的结点上,在结点处处理所有涉及它的询问,然后不保存它的空间。然而这样凸包的复杂度少了,但是把询问拆出来的复杂度变成 O(mlogn) 了。
但这给了我们启发,我们直接带着所有询问向下递归,判断该询问在该区间是否生效,是否下放到左右儿子即可(下放后要释放空间或是直接使用单链表移过去下放!),这样每个时刻每个询问至多只会在两个节点上。空间复杂度 O(n+m)

问题 3

法 0.5:WQS 二分。复杂度 O(n2logn)

法一:模拟费用流。复杂度 O(nlogn),每多流一个就是更大的答案。

法二:凸优化 DP。

闵和、凸优化常和分治/区间 DP 搭配,因为凸壳合并可以线性,分治 logn 层,总复杂度就是 O(nlogn) 的。本题是一个例子。

f[l,r,0/1,0/1](x) 表示在 [l,r] 区间选 x 个互不相交的段,l 选不选、r 选不选 的最大和。令 dp[l,r](x)=maxf[l,r,?,?](x)。根据费用流模型可以知道 f,dp 都是凸的。

显然 f[l,r,?,?] 可以从 f[l,m,?,?]f[m,r,?,?] 做 <max,+> 卷积得到,根据闵和的凸性质,可以线性合并。对应的,dp[l,r] 也可以这么合并。一路合并上来可以得到 dp[1,n](1n) 的凸壳。

问题 4

先考虑一个查询 (l,r,k),退化到了问题 1,使用 wqs 二分即可解决。

然后沿用问题 2 的思路,在线段树上做这个问题。

考虑在一个线段树结点上怎么快速求答案。我们发现可以再利用问题 3 的想法:在每个线段树结点上记录这个结点对应的 dp[l,r]() 的凸包,预处理复杂度 O(nlogn)。然后在这个凸包上二分即可。

然后对于一次询问 (L,R,k),我们想要把它拆成若干个线段树结点,各自求答案合并。但是问题来了:我们还限制了只能取 k 段,各自求肯定无法满足这个条件。怎么办?如果这些结点的凸包再合并,复杂度立刻变成平方。

既然段数是个棘手的限制,我们就想办法去掉它。在问题 1 里也有段数限制,是用 wqs 二分解决的。所以在这里我们也 wqs 二分,这样就可以每个线段树结点内各自取若干段了。复杂度 O(nlogn+qlogVlog2n):wqs 二分一个 log,线段树一个 log,凸包二分一个 log。强制在线的复杂度目前到此为止了。

如果允许离线,还能更快!使用整体二分,即 wqs 二分时把当前位置的询问分成两半各自传给下面,可以少一个 log

posted @   FLY_lai  阅读(49)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示