[Codeforces 922E]Birds

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; }
原文地址:https://www.cnblogs.com/NaVi-Awson/p/8682134.html