绿色通道
考虑二分答案:
$f_i$ 表示完成前 $i$ 个后的最少耗时,就有转移式:
$$f[i] = \max(f[j]) + a[i], i - mid\leq j \leq i-1$$
最后检查 $f[n]$ 是否 $\leq t$ 就行了
直接做 $O(n^2)$,优化:
-
线段树 $O(n log_2n)$:
- 每次求完 $f[i]$ 就在 $i$ 这个位置上加上 $f[i]$ (单点修改)。
- 求 $f[i]$ 时从线段树中查询 $i - mid$ 到 $i - 1$ 的和(区间查询)。
线段树板子,这个还是挺简单的。
-
单调队列 $O(n)$:
- 用一个单调递增的队列记录 $i - mid$ 到 $i - 1$ 的值。
- 求完 $f[i]$ 后,就考虑把 $i$ 插入队列:若 $f[i]$ 比队尾元素的值小,则弹出队尾元素,直到 $f[i]$ 队尾元素的值大,再从尾部插入 $i$。
- 求 $f[i]$ 时,就考虑取对头元素:若队头元素大与于 $i - mid$,即不在区间内,弹出队头元素,直到在 $[i-mid,i-1]$ 区间内,则上式的 $j$ 就是队头,更新一下就行了。
代码不长,乱搞就行了。
本蒟蒻写了单调队列优化,代码如下 (有点丑):
#include <cstdio>
#include <iostream>
using namespace std;
const int maxn = 5e4 + 10;
int w[maxn];
int n, t, f[maxn];
int head, tail, q[maxn];
bool check (int x) {
f[0] = 0;
head = 0; tail = -1;
for (int i = 1; i <= x + 1; ++i) {
f[i] = w[i];
while (head <= tail && f[q[tail]] >= f[i]) tail --;
q[++tail] = i;
}
for (int i = x + 2; i <= n + 1; ++i) {
while (head <= tail && q[head] < i - x - 1) head ++;
f[i] = f[q[head]] + w[i];
while (head <= tail && f[q[tail]] >= f[i]) tail --;
q[++tail] = i;
}
return f[n + 1] <= t;
}
int main() {
scanf ("%d%d", &n, &t);
for (int i = 1; i <= n; ++i) {
scanf ("%d", &w[i]);
}
int l = 1, r = n;
while (l < r) {
int mid = (l + r) >> 1;
if (check (mid)) r = mid;
else l = mid + 1;
}
printf ("%d", l);
return 0;
}