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\)

  合并的细节要看代码。