一道挺妙的题-CF671E
CF671E-%你赛T4
题意:
有 \(n\) 个点按照 \(1,2,\dots,n\) 的顺序排列。对于每个 \(i \in [1,n-1]\),点 \(i\) 和点 \(i+1\) 之间的距离为 \(w_i\)。
每个点 \(i\) 有一个非负整数 \(g_i\)。每当车辆到达点 \(i\) 时,会立即获得 \(g_i\) 单位燃料。车辆的油箱容量没有限制,每行驶 \(1\) 单位距离消耗 \(1\) 单位燃料。
你可以额外分配至多 \(k\) 单位燃料。
对于一对满足 \(1 \le l \le r \le n\) 的点 \((l,r)\),进行如下两个相互独立的阶段:
- 第一阶段中,车辆从点 \(l\) 出发,只向右行驶,依次经过
- 第二阶段中,车辆从点 \(r\) 出发,只向左行驶,依次经过
每个阶段开始时,车辆的油箱均为空,并立即获得该阶段起点处的燃料。之后,每到达一个点,也会立即获得该点的全部燃料。
若在两个阶段中,车辆都能在燃料耗尽之前到达终点,则称区间 \([l,r]\) 是可行的。
区间 \([l,r]\) 的价值定义为
请你合理分配至多 \(k\) 单位额外燃料,并选择一个可行区间,使其价值最大。
做法:
记 \(sumg_i\) 表示油量的前缀和, \(sumw_i\) 记为距离的前缀和。
首先考虑从 \(l\) 行驶到 \(r\) 的最优策略 , 先考虑最少分配多少燃料才能到达 \(r\) 。
假设途中路过一点 \(i\) ,那么:
获得的油量:
油耗:
如果某一点 \(j\) 时获得的油量小于油耗,就需要在之前某个点添加油。
为了便利以后从 \(r\) 返回 \(l\) 的时候且不浪费油(从 \(r\) 返回时油量会清零),添加油的点一定是 \(j-1\) ,且添加的油量一定是刚好够车行驶到 \(j\) 。
那么我们就会发现,对于固定的左端点 \(l\) ,放置油的点也一定是固定的,如:

然后我们考虑从右端点返回时的最优策略。
显然我们肯定会把能添加的油都添加到右端点 \(r\) 上。
那么应该至少添加多少油呢?
假设在 \(r\) 添加的油量为 \(t\) ,从 \(l\) 走到 \(r\) 后每个点添加过后的总油量为 \(g'\) ,新的 \(g'\) 前缀和数组为 \(sumg'\) , 添加的总油量为 \(c_r=sumg'_{r-1}-sumg_{r-1}\)。
途中路过一点 \(i\) ,那么:
获得的油量:
消耗的油量:
那么对于每个 \(i\) ,都需要有:
即:
也就是说:
那么在顺利返回的前提下,\(t\) 的最小值为:
所以总消耗的分配油量:
由于 \(c_r=sumg'_{r-1}-sumg_{r-1}\) ,所以 \(sumg'_{r-1}=sumg_{r-1}+c_r\) ,代入替换,得:
合并 \(g_r,sumg_{r-1}\) 得 \(sumg_r\) 并将 \(c_r\) 传入 \(\max\):
这个式子要小于 \(k\) 。
至此我们整理一下思路。
已知:
-
对于某一个左端点 \(l\) ,\(sumg'\) 是固定的。在此基础上,对于每个右端点而言 \(c_r\) 也可以被确定。
-
对于某个区间,判定是否合法只用判断总消耗的分配油量是否小于等于 \(k\) 。
-
我们要找到最长的一个区间使得它合法。
那么我们就有一个思路:
对于每个左端点,找到最靠右的合法右端点。
形式化的,设 \(A_i=sumg'_i-sumw_{i-1}\) ,\(B_i=sumw_{i-1}-sumg_{i}\) , \(C_i=sumg'_{i}-sumg_{i}\)。
对于每个 \(l\) ,我们需要找到最大的 \(r\) ,使得:
这实质上是两个条件:
-
\(C_r\le k\)
-
$\max_{l\le i\le r}(A_i) +B_r\le k $
对于一个左端点 \(l\) 而言,我们可以维护 \(A,C\) 的最大值,\(B\) 的最小值(假设能维护,后文再说)。然后考虑查询:
如果只考虑 \(C_r\) ,显然很好找到最大的 \(r\) 。
而由于 \(C\) 单调不减,所以假设最大的 \(r\) 是 \(R\) ,那么所有 \(r\in[l,R]\) 都满足条件。
然后我们在这个范围内继续考虑第二个条件:
首先我们注意 \(A_i+B_i\le k\) 恒成立。
这是因为 \(sumg'_i-sumw_{i-1}+sumw_{i-1}-sumg_{i}=sumg'_i-sumg_{i}\)
而 \(sumg'_i-sumg_i=C_i\le C_r \le k\)
那么假设 \([l,R]\) 中 \(A_i\) 的最大值的下标为 \(t\) ,如图:

那么我们就知道了区间 \([l,t]\) 一定合法,由于只询问最长的区间,所以我们根本不需要去管左端点为 \(l\) ,右端点小于 \(t\) 的区间了。
也就是说,我们还需要去找 \([l,r]\) 合不合法,其中 \(r\in[t+1,R]\) 。
而对于这些区间 \(\max_{l\le i\le r} A_i\) 就是 \(A_t\) 。
所以问题就是要求最大的 \(r\) 使得 \(A_t+B_r\le k\) 成立。
这个怎么办呢,就是直接在线段树上二分解决。
此处虽然 \(B_r\) 并不单调,但对于线段树的每个节点,可以存下其管辖区间内的 \(B_r\) 的最小值。然后如果右子树的最小值小于等于 \(k-A_t\) 那就去右端点,如果右端点没查到或者右端点最小值大于 \(k-A_t\) 就去左端点。
那时间复杂度正确吗?没有问题!还是 \(O(n\ log\,n)\) !

如图,假设一直往右走却一直没查询到,这时如果左子树的 \(min\le k-A_t\) 那么就会去左子树,而此时这个区间已经被查询区间 \([t,R]\) 完全包含在内了,也就是说一定能查到值。而如果左子树的 \(min>k-A_t\) 那么根本就不会进入左子树,所以一定是对的。
这样,查询的问题就解决了。
接下来,我们需要考虑当 \(l\) 变化的时候,怎么维护 \(A,B,C\) ,这几个都和 \(sumg'\) 有关,所以主要是需要维护 \(sumg'\) 。
首先,我们需要观察一下对于某个 \(l\) 而言,\(sumg'\) 相比 \(sumg\) 增加了哪些部分。
还是刚刚的那个例子:

对于区间左端点 (记为 \(1\) ,后面依次 \(2,3...\) ),显然:
-
\(1\) 处的加油操作给 \(sumg'_{1-5}\) 都增加了 \(2\) 。
-
\(2\) 处的加油操作给 \(sumg'_{2-5}\) 都增加了 \(4\) 。
-
\(4\) 处的加油操作给 \(sumg'_{4-5}\) 都增加了 \(1\) 。
那么我们发现 \(2\) 处的 \(sumg'\) 的值从 \(4\) 处转移是容易的(后缀加即可)。
\(1\) 处的 \(sumg'\) 的值从 \(2\) 处转移也是容易的。
那么我们根据这样的关系,建立一棵树,具体的,记点 \(i\) 在油箱为空时能够达到的最远点为 \(j\) ,则从 \(i\) 向 \(j+1\) 连一条边。
本例中建出的树是这样的:

这样,就可以在 \(DFS\) 的时候就可以方便的维护 \(sumg'\) 了。
至此,本题结束。

浙公网安备 33010602011771号