P11678 [USACO25JAN] Watering the Plants P 题解

Description

Bessie 的花园中有 \(N\) 株植物,从左到右编号为 \(1\)\(N\)\(2\leq N\leq 5\cdot 10^5\))。Bessie 知道植物 \(i\) 至少需要 \(w_i\)\(0\leq w_i \leq 10^6\))单位的水。

Bessie 有一个十分古怪的灌溉系统,包含 \(N-1\) 条水渠,编号为 \(1\)\(N-1\)。每条水渠 \(i\) 有一个相关的单位费用 \(c_i\)\(1\le c_i\le 10^6\)),Bessie 可以支付费用 \(c_i k\) 来为植物 \(i\)\(i+1\) 各提供 \(k\) 单位的水,其中 \(k\) 是一个非负整数。

Bessie 很忙,可能没有时间使用所有的水渠。对于每一个 \(2\leq i \leq N\),计算仅使用前 \(i-1\) 条水渠灌溉植物 \(1\)\(i\) 所需要的最小费用。

Solution

首先这类题有两种做法,一种是贪心/反悔贪心,另一种是 dp。由于这题条件很好刻画,所以考虑 dp。

容易想到设 \(f_{i,j}\) 表示把 \(c_{[1,i-1]}\) 的状态确定,让 \([1,i-1]\) 的植物已经满足条件,并且第 \(i\) 个植物还需要 \(j\) 单位的水的最小代价。转移代码如下:

memset(f[o], 0x3f, sizeof(f[o]));
for (int j = 1; j <= 200; ++j) chkmin(f[o ^ 1][j], f[o ^ 1][j - 1]);
for (int j = 0; j <= 200; ++j) f[o ^ 1][j] += c[i - 1] * j;
for (int j = 200; ~j; --j) chkmin(f[o ^ 1][j], f[o ^ 1][j + 1]);
for (int j = 0; j <= a[i]; ++j) f[o][j] = f[o ^ 1][a[i] - j];

具体含义就是先取前缀 min,再让 \(f_{i-1,j}\leftarrow f_{i-1,j}+c_{i-1}\cdot j\),此时 \(f_{i-1,j}\) 的意义是 \(c_{i-1}\) 恰好\(j\) 次的最小代价,再取一遍后缀 min 后,在 \([0,a_i]\) 值域内翻转就得到了 \(f_{i,*}\) 的状态。

经过打表会发现 \(i\geq 3\)\(f_{i,*}\) 在做完任何操作后都是凸的,所以直接用 deque 维护差分数组即可,全局翻转时打个标记即可。

时间复杂度:\(O(n)\)

Code

#include <bits/stdc++.h>

#define int int64_t

const int kMaxN = 5e5 + 5;

int n, v = 1e6;
int a[kMaxN], c[kMaxN];

inline void chkmax(int &x, int y) { x = (x > y ? x : y); }
inline void chkmin(int &x, int y) { x = (x < y ? x : y); }

struct Queue {
  int f0, dir, sumd, sumc, opd, detd;
  // dir = 0 : 正向,dir = 1 : 反向
  // sumd : d 的和,sumc : c 的和
  // [d, c] 的实际值是 d * opd + detd
  std::deque<std::pair<int, int>> q;
  // [d, c] : [差分, 数量]
  void init() {
    f0 = c[1] * std::max(a[1], a[2]);
    dir = 0, opd = 1, detd = 0;
    if (a[1] < a[2]) {
      q.emplace_back(-c[1], a[2] - a[1]);
      q.emplace_back(0, v - (a[2] - a[1]));
      sumd = -c[1] * (a[2] - a[1]), sumc = v;
    } else {
      q.emplace_back(0, v);
      sumd = 0, sumc = v;
    }
  }
  void fix() { // 末尾加 0
    if (dir == 0) {
      if (sumc < v) q.emplace_back(-detd * opd, v - sumc);
      sumd += -detd * opd * (v - sumc), sumc += v - sumc;
    } else {
      if (sumc < v) q.emplace_front(-detd * opd, v - sumc);
      sumd += -detd * opd * (v - sumc), sumc += v - sumc;
    }
  }
  void getpremin() {
    assert(sumc == v);
    if (dir == 0) {
      for (; !q.empty() && q.back().first * opd + detd >= 0; q.pop_back()) {
        auto [d, c] = q.back();
        sumd -= d * c, sumc -= c;
      }
      fix();
    } else {
      for (; !q.empty() && q.front().first * opd + detd >= 0; q.pop_front()) {
        auto [d, c] = q.front();
        sumd -= d * c, sumc -= c;
      }
      fix();
    }
  }
  void getsufmin() {
    assert(sumc == v);
    if (dir == 0) {
      for (; !q.empty() && q.front().first * opd + detd <= 0; q.pop_front()) {
        auto [d, c] = q.front();
        sumd -= d * c, sumc -= c, f0 += c * (d * opd + detd);
      }
      if (sumc < v) q.emplace_front(-detd * opd, v - sumc);
      sumd += -detd * opd * (v - sumc), sumc += v - sumc;
    } else {
      for (; !q.empty() && q.back().first * opd + detd <= 0; q.pop_back()) {
        auto [d, c] = q.back();
        sumd -= d * c, sumc -= c, f0 += c * (d * opd + detd);
      }
      if (sumc < v) q.emplace_back(-detd * opd, v - sumc);
      sumd += -detd * opd * (v - sumc), sumc += v - sumc;
    }
  }
  void addtag(int v) { detd += v; }
  void reverse() { f0 += sumd * opd + sumc * detd, dir ^= 1, opd *= -1, detd *= -1; }
  void del(int x) {
    assert(sumc == v);
    if (dir == 0) {
      for (; !q.empty() && sumc > x;) {
        auto [d, c] = q.back();
        int t = std::min(sumc - x, c);
        sumd -= d * t, sumc -= t, c -= t;
        q.pop_back();
        if (c) q.emplace_back(d, c);
      }
      assert(sumc == x);
      reverse(), fix();
    } else {
      for (; !q.empty() && sumc > x;) {
        auto [d, c] = q.front();
        int t = std::min(sumc - x, c);
        sumd -= d * t, sumc -= t, c -= t;
        q.pop_front();
        if (c) q.emplace_front(d, c);
      }
      assert(sumc == x);
      reverse(), fix();
    }
  }
} q;

void dickdreamer() {
  std::cin >> n;
  for (int i = 1; i <= n; ++i) std::cin >> a[i];
  for (int i = 1; i < n; ++i) std::cin >> c[i];
  q.init();
  std::cout << q.f0 << '\n';
  for (int i = 3; i <= n; ++i) {
    q.addtag(c[i - 1]), q.getsufmin(), q.del(a[i]), q.getpremin();
    std::cout << q.f0 << '\n';
  }
}

int32_t main() {
#ifdef ORZXKR
  freopen("in.txt", "r", stdin);
  freopen("out.txt", "w", stdout);
#endif
  // std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
  int T = 1;
  // std::cin >> T;
  while (T--) dickdreamer();
  // std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
  return 0;
}
posted @ 2025-11-07 15:00  下蛋爷  阅读(5)  评论(0)    收藏  举报