Description
一条直线上有 (n) 棵树,每棵树上有 (c_i) 只鸟,在一棵树底下召唤一只鸟的魔法代价是 (cost_i) 每召唤一只鸟,魔法上限会增加 (B) 。从一棵树走到另一棵树,会增加魔法 (X) ,一开始的魔法和魔法上限都是 (W) 。问最多能够召唤的鸟的个数。
(1leq nleq 1000,1leq B,X,Wleq 10^9,1leq sum_{i=1}^n c_ileq 10000)
Solution
容易想到记 (f_{i,j}) 为第 (i) 棵树时,召唤了 (j) 只鸟,剩余魔法的最大值。
转移的话就是枚举 (j-c_i) 范围内的上一棵树的最大值。
但这样复杂度似乎不太漂亮,考虑用单调队列优化,一个显然的“滑动窗口”模型。复杂度为 (O(nsum 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
", ans);
}
int main() {work(); return 0; }