CF1175G - Yet Another Partiton Problem
CF1175G - Yet Another Partiton Problem
题目大意
给定序列 \(a_i\) ,现在将其分成 \(k\) 段,每段 \([l,r]\) 的权值定义为 \((r-l+1)\max\{a_{l..r}\}\)
求最小化权值总和
分析
显然有 \(\mathbb{Naive}\) 的 \(dp\)
\(dp_{i,j}\) 表示前 \(i\) 个分了 \(j\) 段的答案,直接做复杂度为 \(O(n^2k)\)
优化它有几种常见思路:
1.贪心简化决策
设每个段最大值 \(b_i\) ,则一个段不能向左边扩展的条件是
两端的 \(b_{i-1}<b_i\) (扩展会亏),或者 \(a_{l_i-1}>b_i\) (扩展会改变 \(b_i\) )
这样能稍微限制一下转移,然而并不好优化
2.决策单调性
枚举区间进行决策的问题通常具有单调性
于是尝试通过分治决策单调性优化到 \(O(nk\log n)\)
然而已经被垃圾数据击毙 尝试证明这是假的
3.斜率优化
考虑确定 \(\max a_i\) 之后,两侧 \(r-l+1\) 就是一个斜率优化的转移形式
考虑如何实现这个斜率优化
首先将 \(dp_{i,j}\) 两个维护交换,按照分段数一层层进行转移
每一层,可以考虑在 \(a_i\) 的笛卡尔树上进行转移
每次考虑所有跨过当前节点的转移区间
那么就要支持:
1.查询左子树 \(dp_l-l \cdot a_u\) 的最小值
2.更新右子树 \(dp'_r+r\cdot a_u\) 的最小值
option1
为了实现1操作,容易想到从子树中合并凸包,或者直接进行区间凸包查询
合并凸包的问题可以暴力李超树合并维护
但事实上区间凸包是可以维护的,方法如下
1.维护一个静态线段树, \(O(n\log n)\) 归并预处理凸包
2.查询 \(a_u\) 递增,具有单调性,可以在每个被查询节点上处理一个指针
复杂度为就是均摊 \(O(n\log n)\)
option2
子树更新答案,可以通过可持久化李超树|李超树区间修改实现
复杂度分别为 \(O(n\log n),O(n\log^2n)\) ,鉴于区间修改常数不满,差别不会太大
但实际上也可以通过朴素凸包实现:
从根开始dfs,每次插入一个 \(x+y \cdot i\) 的线段形式
\(y\) 是递增的,插入具有单调性,可以维护一个栈凸包
每次二分弹掉节点,插入自己
(不能直接弹,因为要支持回撤,会使得原先是均摊 \(O(n)\) 的弹栈操作退化为 \(O(n^2)\) )
查询也可以通过二分解决(这实际上是一个经典树上斜率优化问题)
我当然写了李超树啦
1 | const int N=2e4+10,U=2e4,INF=1e9+10; |