CF671E
首先可以想到尝试使用扫描线,只需对每个 \(r\) 求出最优的 \(l\) 即可。
先假设没有 \(k\) 次修改机会。考虑令 \(a_i = g_i - w_i, b_i = g_i - w_{i - 1}\),即向右 / 向左走时经过点 \(i\) 油量的增量。
记 \(a, b\) 的前缀和分别为 \(S_i, T_i\),于是有:
第二个条件即 \(\forall i \in [l, r - 1], T_i \le T_r\)。
现在我们考虑修改。显然最优的修改方案是把所有 \(k\) 次都用在 \([l, r]\) 区间内。考虑将 \(g_i + 1\) 带来的变化,即 \(S, T\) 数组后缀 \([i, n]\) 加 \(1\)。于是我们要求的就是对每个 \(r\),进行 \(k\) 次这样的后缀加后能得到的最远的 \(l\)。
设 \(r\) 左侧第一个 \(T_i \gt T_r\) 的 \(i\) 为 \(lef_r\),则根据第二个限制,\(l \in (lef_r, r]\),显然我们为了让 \(l\) 更远需要贪心地使 \(lef_r\) 左移,而对于一个 \(lef_r\),左移的代价是 \(T_{lef_r} - T_r\) 次操作(这些操作只需满足 \(i \in (lef_r, r]\) 即可)。注意到进行这些操作后 \(T_r\) 就变成了原本的 \(T_{lef_r}\),以此类推,我们可以得出最远能扩展到的 \(lef_r\) 是最小的 \(i\),使得 \(T_i - T_{r} \le k\)。可以看出我们扩展的过程相当于单调栈弹栈的过程,所以直接在 \(r\) 处的单调栈上二分即可。
再考虑第一个限制,假设 \(r\) 处的单调栈为 \(sta_1, sta_2, \dotsb, sta_{k}\),其中 \(sta_k = r\),假如答案 \(l\) 在 \([sta_i + 1, sta_{i + 1}]\) 区间里,则 \(S_{l - 1}\) 需满足 \(S_{l - 1} = \min_\limits{j = l - 1}^{r - 1} S_j\),那么显然之前将 \(lef_r\) 向左扩展直到 \(sta_i\) 的所有操作都可以贪心地向左放(因为这样可以保证 \([l, r - 1]\) 的 \(S\) 增加得更多),也就是说,\(lef_r\) 从 \(sta_{j + 1}\) 扩展到 \(sta_{j}\) 的时候,我们可以将 \(T_{sta_{j}} - T_{sta_{j + 1}}\) 次操作都放在 \(sta_{j} + 1\) 处完成。
但是 \(S\) 的这些后缀加不好维护(需要一边扩展 \(lef_r\) 一边进行这些修改),所以我们考虑把 \(S\) 数组 \([i, n]\) 的后缀加 1 变成 \([1, i - 1]\) 的前缀减 1。注意到这样对于 \(sta_{1 \sim i - 1}\),其对前缀的改变就不会影响到我们判定 \(S_{l - 1} = \min_\limits{j = l - 1}^{r - 1} S_j\) 了(可以画个图手搓一下这部分,可能比较难理解)。于是前一段提到的贪心策略变成了每次将前缀 \([1, sta_{j}]\) 减去 \(T_{sta_j} - T_{sta_{j - 1}}\)。
注意到我们可以在维护 \(r\) 处的单调栈的同时进行这些修改,每次向栈里插入一个元素就进行上述前缀减,弹出则取消这部分贡献即可。用线段树维护进行这些修改后的 \(S\) 数组,记为 \(S_0\)。
接下来考虑怎么求 \(l\),假设 \(l \in [sta_i + 1, sta_{i + 1}]\),将 \(lef_r\) 扩展到 \(sta_{i}\) 后剩下了 \(k_0\) 次操作,那么显然最优的方案是对 \([1, l - 1]\) 进行 \(k_0\) 次前缀减,注意到此时 \(S_{l - 1}\) 一定是被减了恰好 \(k\),所以只需判断 \(S_{0}[l - 1] = S_{l - 1} - k \le \min_\limits{j = l}^{r - 1} S_0[j]\) 即可。
形式化地,我们需要维护一个数组 \(S_0\),再给定一个数组 \(S\),支持 \(S_0\) 前缀加、查询最小的满足 \(-S_{l - 1} + \min_\limits{j = l}^{r - 1} S_0[j] \ge -k\) 的 \(l\)。于是这就转化成了单侧递归线段树能维护的模型。
代码咕了.jpg

浙公网安备 33010602011771号