Loading

CF1566F

感觉好难。

首先,注意到点不会互相穿过,因为穿过了替换就行,意味着相对顺序不会改变。

那么一个点最多掉头一次,并且一个点的活动范围必须在其左右两个点之间。

这好像已经可以拿来 dp 了。设一个点的活动范围为 \([x-l, x+r]\),其中 \(x\) 为初始位置,则花费的代价为 \(2 \min\{l, r\} + \max\{l, r\}\)

每一条线段都必须被覆盖到。先把没用的线段去掉,再注意到如果有重叠只用满足小的那条线段就可以了。那么现在只关注相邻的两个点和中间的很多条线段,这些线段的左端点和右端点都递增,枚举一个分界点,左边的线段用左侧的那个点覆盖,右边的线段用右边的那个点覆盖,也就是可以确定一个点的活动范围了。

那么 dp 时再记一下是否已经有那个 *2 了,枚举相邻的点确定活动范围转移。

感觉用确定范围更直观一些,用是否 *2 来表示也比先走或者先右更好一些。

#include <bits/stdc++.h>

using LL = long long;

void solve() {
  int n, m; scanf("%d %d", &n, &m);
  std::vector<int> a(n);
  std::vector<std::pair<int, int>> p(m);
  for (int i = 0; i < n; i++) scanf("%d", &a[i]);
  for (auto &[x, y] : p) scanf("%d %d", &x, &y);
  std::sort(a.begin(), a.end());
  std::sort(p.begin(), p.end(), [&](auto x, auto y) {
    return x.first != y.first ? x.first < y.first : x.second > y.second;
  });
  std::vector<std::pair<int, int>> b;
  int mn = 1e9 + 1;
  for (int i = m - 1; i >= 0; i--) {
    int t = std::lower_bound(a.begin(), a.end(), p[i].first) - a.begin();
    if (p[i].second >= mn) continue;
    if (t != n && p[i].first <= a[t] && a[t] <= p[i].second) continue;
    b.push_back(p[i]), mn = p[i].second;
  }
  std::reverse(b.begin(), b.end());
  m = b.size();
  if (!m) return printf("0\n"), void();
  std::vector<std::vector<LL>> f(n, std::vector<LL>(2, 1e18));
  for (int i = 0, l = 0, r = 0; i < n; i++) {
    l = r;
    while (r < m && b[r].second < a[i]) ++r;
    if (!i) {
      int disl = std::max(a[0] - b[0].second, 0);
      f[i][0] = disl, f[i][1] = 2ll * disl;
      continue;
    }
    for (int j = l; j <= r; j++) {
      int disl = j == l ? 0 : b[j - 1].first - a[i - 1];
      int disr = j == r ? 0 : a[i] - b[j].second;
      f[i][0] = std::min({f[i][0], f[i - 1][1] + disl + disr, f[i - 1][0] + 2ll * disl + disr});
      f[i][1] = std::min({f[i][1], f[i - 1][1] + disl + 2ll * disr, f[i - 1][0] + 2ll * disl + 2ll * disr});
    }
  }
  int disr = std::max(b[m - 1].first - a[n - 1], 0);
  printf("%lld\n", std::min(f[n - 1][0] + 2ll * disr, f[n - 1][1] + disr));
}

int main() {
  int T; scanf("%d", &T); while (T--) {
    solve();
  }
} 
posted @ 2025-06-05 09:59  purplevine  阅读(13)  评论(0)    收藏  举报