Gym102331H Honorable Mention(300iq Contest 2) 题解
题面
给定一个长度为 \(n\) 的数组 \(\{a_i\}\),和 \(q\) 组询问 \((l, r, k)\) ,表示:
- 求在 \([l, r]\) 这个子数组中恰好选择 \(k\) 个不相交的段,使其权值和最大。
输出最大权值和。
\(n, q, a_i \le 35000\)。
题解
数据结构
整体二分
凸性
线段树
wqs 二分
首先,对于序列 \(\{a_i\}\) ,设恰好选择 \(k\) 个不交子段的最大权值为 \(f(k)\),那么我们可以发现 \(f(k)\) 是一个凸函数。
证明:
考虑一种模拟费用流做法,每次选择一个最大子段,然后将这个子段取负,不断进行 \(k\) 轮,所有取出的子段和就是答案。
注:当 \(k\) 比这个序列中所有正数个数都大的时候,答案就是将所有数从大到小排列,依次选 \(k\) 个的答案。不特判这个的话 \(k\) 较大的时候会寄。
然后我们可以在线段树每个节点上面维护 \(f(0 / 1, 0 / 1)\),表示 (左边是否相邻,右边是否相邻) 的 \(f\) 函数的取值,pushup
的时候,可以对于凸函数合并,然后对于两个都相邻的情况集体位移。
询问的话,可以拆成 \(\log\) 个区间,但是我们现在不支持区间的合并。
根据凸函数的套路,我们可以使用 wqs
二分,二分一个斜率 \(k\),然后在线段树的节点上二分找到对应的取值,接着讨论一下和之前端点是否相邻,合并一下即可。
复杂度就是 \(\mathcal O(n \log^3n)\)。
但是还可以更优,我们可以使用整体二分(更形象地说,应该是 bfs
版的整体二分,这样总共进行 \(\log\) 轮),每一轮,按照斜率从大到小计算当前答案,同时在线段树上面维护一个单调的指针,表示当斜率为 \(k\) 的时候应该切到函数的哪一个位置,然后合并和之前一样。
所有指针的总移动次数是 \(\mathcal O(n\log^2n)\) 的,于是少了一个 \(\log\)。
合并的细节要看代码。