题解:P12196 [NOISG2025 Prelim] Lasers 2

题解:P12196 [NOISG2025 Prelim] Lasers 2

题目描述

Pavement 从 Gohcart 那里购买了一个激光玩具(Gohcart 原先是从 Rar the Cat 那里买的)。这个玩具有一个由 \(h\)\(w\) 列组成的网格。网格的行编号从上到下为 \(1\)\(h\),列编号从左到右为 \(1\)\(w\)

每一行恰好包含一个上锁的滑动墙。初始时,第 \(i\) 行的墙覆盖第 \(l[i]\) 列到第 \(r[i]\) 列(从 \(1\) 开始编号),解锁它需要花费 \(c[i]\) 美元。墙一旦解锁,就可以在第 \(i\) 行上水平滑动到任意位置,只要它对齐在网格的边缘内即可。墙的任何部分都不能超出玩具的左边缘或右边缘。

每一列的顶部都有一个朝下的激光。如果某个滑动墙位于第 \(i\) 列,那么它将阻挡第 \(i\) 列上的激光。

一个可能的玩具示例如下,其中 \(h = 3\)\(w = 10\)

给定总预算 \(k\) 美元,Pavement 的目标是在合理解锁并滑动墙壁的情况下,最大化未被阻挡的激光数量。请你确定他最多可以实现多少束未被阻挡的激光。

  • \(1 \leq h, w \leq 2000\)
  • \(0 \leq k \leq 10^9\)
  • 对所有 \(1 \leq i \leq h\),满足 \(1 \leq l[i] \leq r[i] \leq w\)
  • 对所有 \(1 \leq i \leq h\),满足 \(0 \leq c[i] \leq 10^9\)

solution

因为我们可以将所有激活墙全部放在最长的激活墙后面,而如果最长的激活墙比最长未激活墙短(也就是全场最长墙未激活),那么答案就是未激活墙的并的大小,否则我们需要找一个地方放置这个激活的全场最长墙,然后答案是未激活墙的并再并上这个最长墙的大小。

一堆区间的并还是太困难了,我们不妨按照并的连续段进行 dp。我们把问题改成最大化选中的区间的价格的和(也就是我们着眼于的是未激活的墙),先处理一个 \(sum[l,r]\) 表示所有 \(\subseteq[l, r]\) 的区间的价格,然后直接 dp,设 \(f_i\) 表示考虑完 \([1, i]\) 的选择情况的最大价格,转移就 \(O(n)\) 转移 \(f_i=\max_{j\leq i}f_{j-1}+sum_{j, i}\)。这只是一个雏形,我们还需要的是:

  1. 需要有一个连续段的长度大于全场最长墙,然后再看看能不能计入全场最长墙的价格。使用一个 0/1 记录即可。
  2. 需要记答案,记录有多少个空位(记作变量 \(w\))。使用 \(O(n)\) 的时空代价即可。

此时复杂度 \(O(n^3)\),需要优化。

发现我们将前面的 \(O(n^2)\) 个状态排成矩阵的形式,那么就是形如矩阵的某一列对位加上 \(sum\) 的某一列的最大值贡献到 \(f_{i, w}\),且随着 \(i\) 增加这些列不会变,只有 \(sum\) 的那一列会增加。然而这样的话可以不在一开始处理 \(sum\),而是 \(i\) 增加的时候将右端点为 \(i\) 的区间拿出来将每列的某个前缀进行整体加。所以我们对每一列用线段树维护即可,需要支持区间加和区间最大值(因为有一个“连续段的长度大于全场最长墙”的特殊转移需要区间最大值)。由于区间个数是 \(O(n)\) 的,复杂度做到 \(O(n^2\log n)\) 也就是 \(O(hw\log w)\)

code

#include <bits/stdc++.h>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr, ##__VA_ARGS__)
#else
#define debug(...) void(0)
#define endl "\n"
#endif
using LL = long long;
template <class T> T& chkmin(T& x, const T& y) { return x = min(x, y); }
template <class T> T& chkmax(T& x, const T& y) { return x = max(x, y); }
constexpr int N = 2010;
struct wall {
  int l, r, c;
  int length() const { return r - l + 1; }
  friend bool operator<(const wall& lhs, const wall& rhs) { return lhs.length() < rhs.length(); }
};
struct segtree {
  LL ans[N << 2], tag[N << 2];
  segtree() {
    memset(ans, ~0x3f, sizeof ans);
    memset(tag, 0, sizeof tag);
  }
  void spread(int p, LL k) { tag[p] += k, ans[p] += k; }
  void maintain(int p) { ans[p] = max(ans[p << 1], ans[p << 1 | 1]); }
  void pushdown(int p) { spread(p << 1, tag[p]), spread(p << 1 | 1, tag[p]), tag[p] = 0; }
  void setValue(int x, LL v, int p, int l, int r) {
    if (l == r) return ans[p] = v, void();
    int mid = (l + r) >> 1;
    pushdown(p);
    if (x <= mid) setValue(x, v, p << 1, l, mid);
    else setValue(x, v, p << 1 | 1, mid + 1, r);
    maintain(p);
  }
  void modify(int ql, int qr, LL k, int p, int l, int r) {
    if (ql <= l && r <= qr) return spread(p, k);
    int mid = (l + r) >> 1;
    pushdown(p);
    if (ql <= mid) modify(ql, qr, k, p << 1, l, mid);
    if (mid < qr) modify(ql, qr, k, p << 1 | 1, mid + 1, r);
    maintain(p);
  }
  LL query(int ql, int qr, int p, int l, int r) {
    if (ql <= l && r <= qr) return ans[p];
    int mid = (l + r) >> 1;
    pushdown(p);
    LL ret = -1e18;
    if (ql <= mid) chkmax(ret, query(ql, qr, p << 1, l, mid));
    if (mid < qr) chkmax(ret, query(ql, qr, p << 1 | 1, mid + 1, r));
    return ret;
  }
} tr[N][2];
int n, m;
LL lim, f[N][2][N];
vector<wall> rgs[N];
int main() {
#ifndef NF
  cin.tie(nullptr)->sync_with_stdio(false);
#endif
  cin >> m >> n >> lim, lim = -lim;
  wall maxw = {0, -1, 0};
  for (int i = 1, l, r, c; i <= m; i++) cin >> l >> r >> c, lim += c, rgs[r].push_back({l, r, c}), maxw = max(maxw, wall{l, r, c});
  rgs[maxw.r].push_back({maxw.l, maxw.r, -maxw.c});
  memset(f, ~0x3f, sizeof f);
  f[0][0][0] = 0;
  for (int i = 1; i <= n; i++) {
    f[i][0][0] = 0;
    for (int t : {0, 1}) {
      for (int w = 1; w <= i; w++) {
        auto& seg = tr[w - i + n][t];
        seg.setValue(i, f[i - 1][t][w - 1], 1, 1, n);
        for (auto wl : rgs[i]) seg.modify(1, wl.l, wl.c, 1, 1, n);
        chkmax(f[i][t][w], max(seg.ans[1], f[i - 1][t][w]));
        if (t == 0 && i >= maxw.length()) {
          if (maxw.r <= i) seg.modify(1, maxw.l, maxw.c, 1, 1, n);
          chkmax(f[i][1][w], seg.query(1, i - maxw.length() + 1, 1, 1, n));
          if (maxw.r <= i) seg.modify(1, maxw.l, -maxw.c, 1, 1, n);
        }
      }
    }
  }
  for (int i = 1; i <= n; i++) {
    for (int t : {0, 1}) {
      for (int w = 0; w <= n; w++) if (f[i][t][w] >= 0) debug("f[%d][%d][%d] = %lld\n", i, t, w, f[i][t][w]);
    }
  }
  for (int w = 0; w <= n; w++) if (f[n][1][w] >= lim) return cout << n - w << endl, 0;
  assert(false);
  return 0;
}

以下是暴力代码,可以参考一下。

#include <bits/stdc++.h>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr, ##__VA_ARGS__)
#else
#define debug(...) void(0)
#define endl "\n"
#endif
using LL = long long;
template <class T> T& chkmin(T& x, const T& y) { return x = min(x, y); }
template <class T> T& chkmax(T& x, const T& y) { return x = max(x, y); }
constexpr int N = 2010;
struct wall {
  int l, r, c;
  int length() const { return r - l + 1; }
  friend bool operator<(const wall& lhs, const wall& rhs) { return lhs.length() < rhs.length(); }
};
int n, m;
LL lim, sum[N][N];
vector<pair<int, LL>> f[N][2];
int main() {
#ifndef NF
  cin.tie(nullptr)->sync_with_stdio(false);
#endif
  cin >> m >> n >> lim, lim = -lim;
  wall maxw = {0, -1, 0};
  for (int i = 1, l, r, c; i <= m; i++) cin >> l >> r >> c, lim += c, sum[l][r] += c, maxw = max(maxw, wall{l, r, c});
  sum[maxw.l][maxw.r] -= maxw.c;
  for (int l = n; l >= 1; l--) {
    for (int r = 1; r <= n; r++) sum[l][r] += sum[l + 1][r];
  }
  for (int l = 1; l <= n; l++) {
    for (int r = 1; r <= n; r++) sum[l][r] += sum[l][r - 1];
  }
  f[0][0].emplace_back(0, 0);
  for (int i = 1; i <= n; i++) {
    static LL tmp[2][N];
    for (int t : {0, 1}) {
      fill(tmp[t], tmp[t] + i + 1, -1e18); 
      for (auto [w, sv] : f[i - 1][t]) chkmax(tmp[t][w], sv);
      for (int j = i; j >= 1; j--) {
        for (auto [w, sv] : f[j - 1][t]) chkmax(tmp[t][w + i - j + 1], sv + sum[j][i]);
      }
    }
    for (int j = i - maxw.length() + 1; j >= 1; j--) {
      for (auto [w, sv] : f[j - 1][0]) chkmax(tmp[1][w + i - j + 1], sv + sum[j][i] + (j <= maxw.l && maxw.r <= i ? maxw.c : 0));
    }
    for (int t : {0, 1}) {
      for (int w = 0; w <= i; w++) if (tmp[t][w] >= 0) debug("f[%d][%d][%d] = %lld\n", i, t, w, tmp[t][w]);
      LL pre = -1;
      for (int j = 0; j <= i; j++) if (pre < tmp[t][j]) f[i][t].emplace_back(j, pre = tmp[t][j]);
    }
  }
  for (auto [w, sv] : f[n][1]) if (sv >= lim) return cout << n - w << endl, 0;
  assert(false);
  return 0;
}
posted @ 2025-02-25 22:24  caijianhong  阅读(235)  评论(1)    收藏  举报