Description
一条直线上有 $n$ 棵树,每棵树上有 $c_i$ 只鸟,在一棵树底下召唤一只鸟的魔法代价是 $cost_i$ 每召唤一只鸟,魔法上限会增加 $B$ 。从一棵树走到另一棵树,会增加魔法 $X$ ,一开始的魔法和魔法上限都是 $W$ 。问最多能够召唤的鸟的个数。
$1\leq n\leq 1000,1\leq B,X,W\leq 10^9,1\leq \sum_{i=1}^n c_i\leq 10000$
Solution
容易想到记 $f_{i,j}$ 为第 $i$ 棵树时,召唤了 $j$ 只鸟,剩余魔法的最大值。
转移的话就是枚举 $j-c_i$ 范围内的上一棵树的最大值。
但这样复杂度似乎不太漂亮,考虑用单调队列优化,一个显然的“滑动窗口”模型。复杂度为 $O(n\sum c)$ 的。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1000, C = 10000;
long long f[N+5][C+5], n, w, b, x, c[N+5], cost[N+5], tolc;
int q[C+5], head, tail; bool v[N+5][C+5];
void work() {
scanf("%I64d%I64d%I64d%I64d", &n, &w, &b, &x);
for (int i = 1; i <= n; i++) scanf("%I64d", &c[i]), tolc += c[i];
for (int i = 1; i <= n; i++) scanf("%I64d", &cost[i]);
f[0][0] = w; v[0][0] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= tolc; j++) f[i-1][j] = min(f[i-1][j]+x, w+b*j);
head = tail = 0;
for (int j = 0; j <= tolc; j++) {
if (v[i-1][j]) {
while (head < tail && f[i-1][q[tail-1]]+cost[i]*q[tail-1] <= f[i-1][j]+cost[i]*j) --tail;
q[tail++] = j;
}
while (head < tail && j-q[head] > c[i]) ++head;
if (head < tail && f[i-1][q[head]]+cost[i]*q[head]-cost[i]*j >= 0)
f[i][j] = f[i-1][q[head]]+cost[i]*q[head]-cost[i]*j, v[i][j] = 1;
}
}
int ans = 0;
for (int i = 0; i <= tolc; i++) if (v[n][i]) ans = i;
printf("%d\n", ans);
}
int main() {work(); return 0; }