ABC 365 F Takahashi on Grid 做题笔记

Published on

题目

题解

倍增做法

假设起点在左边,终点在右边。

首先最优的走法一定是能往右走就往右走,不能往右走了,我们就称“撞墙”了。

考虑一次撞墙事件,撞墙后,你一定会跑到新区间的上端点或者下端点,然后继续走。

那么撞墙一次之后,我们就可以倍增了。设 $f_{i, j}$ 表示从 $i$ 点出发,撞墙 $2^j$ 后到达的点,$g_{i, j}$ 表示花费的步数。

于是现在问题变成了求第一次撞墙撞在哪,在 ST 上二分或者线段树二分可以做到 $\log n$。

总复杂度 $O\left(n \log n + q \log n\right)$。

代码

代码

/* C++17 is required!
 * By Koicy (https://koicy.ly)
 * Email n@rbtr.ee
 * I've reached the end of my fantasy.

         __    __                             __         _
   _____/ /_  / /_________  ___              / /______  (_)______  __
  / ___/ __ \/ __/ ___/ _ \/ _ \   ______   / //_/ __ \/ / ___/ / / /
 / /  / /_/ / /_/ /  /  __/  __/  /_____/  / ,< / /_/ / / /__/ /_/ /
/_/  /_.___/\__/_/   \___/\___/           /_/|_|\____/_/\___/\__, /
                               SIGN                         /___*/
#ifndef XCODE
constexpr bool _CONSOLE = false;
#else
constexpr bool _CONSOLE = true;
#endif
#define __NO_MAIN__ false
#define __ENABLE_RBLIB__ true
constexpr bool _MTS = false, SPC_MTS = false;
constexpr char EFILE[] = "";
#define FULL(arg) arg.begin(), arg.end()

// :/

signed STRUGGLING([[maybe_unused]] unsigned long TEST_NUMBER) {
  tp n = bin;
  vector<tp> u(n + 1), d(n + 1);
  for (tp i = 1; i <= n; ++i) bin >> u[i] >> d[i];
  vector jmp(n * 2 + 2, vector<pair<tp, tp>>(19));
  struct MAX { tp operator()(tp a, tp b) { return max(a, b); } };
  struct MIN { tp operator()(tp a, tp b) { return min(a, b); } };
  lib::ST<tp, MAX> mav(u);
  lib::ST<tp, MIN> miv(d);
  auto gos = [&](tp t, tp s, tp b) {
    tp l = s, r = b, tar = s;
    while (l <= r) {
      tp mid = (l + r) / 2;
      if (mav(s, mid) <= t && miv(s, mid) >= t) {
        tar = mid;
        l = mid + 1;
      } else r = mid - 1;
    }
    return tar;
  };
  [&]() {
    for (tp i = 1; i <= n; ++i) {
      if (mav(i, n) <= u[i] && miv(i, n) >= u[i]) jmp[i * 2][0] = make_pair(2 * n + 2, 0);
      else {
        tp tar = gos(u[i], i, n) + 1;
        if (abs(u[i] - u[tar]) <= abs(u[i] - d[tar])) {
          jmp[i * 2][0] = make_pair(tar * 2, abs(u[i] - u[tar]));
        } else jmp[i * 2][0] = make_pair(tar * 2 + 1, abs(u[i] - d[tar]));
      }
      if (mav(i, n) <= d[i] && miv(i, n) >= d[i]) jmp[i * 2 + 1][0] = make_pair(2 * n + 2, 0);
      else {
        tp tar = gos(d[i], i, n) + 1;
        if (abs(d[i] - u[tar]) <= abs(d[i] - d[tar])) {
          jmp[i * 2 + 1][0] = make_pair(tar * 2, abs(d[i] - u[tar]));
        } else jmp[i * 2 + 1][0] = make_pair(tar * 2 + 1, abs(d[i] - d[tar]));
      }
    }
  }();
  for (tp i = 1; i < 19; ++i) {
    for (tp j = 2; j <= n * 2 + 1; ++j) {
      if (jmp[j][i - 1].first <= n * 2 + 1) {
        jmp[j][i].first = jmp[jmp[j][i - 1].first][i - 1].first;
        jmp[j][i].second = jmp[j][i - 1].second + jmp[jmp[j][i - 1].first][i - 1].second;
      } else jmp[j][i] = jmp[j][i - 1];
    }
  }
  tp q = bin;
  while (q--) [&]() {
    tp xs, ys, xt, yt;
    bin >> xs >> ys >> xt >> yt;
    if (xs > xt) {
      swap(xs, xt);
      swap(ys, yt);
    }
    if (mav(xs, xt) <= ys && miv(xs, xt) >= ys) {
      bin << abs(ys - yt) + abs(xt - xs) << '\n';
      return;
    }
    tp tar = abs(xt - xs);
    [&]() {
      tp p = gos(ys, xs, xt) + 1;
      tp ud = abs(ys - u[p]) > abs(ys - d[p]);
      tar += min(abs(ys - u[p]), abs(ys - d[p]));
      for (tp i = 18; i >= 0; --i) {
        if (jmp[p * 2 + ud][i].first / 2 <= xt) {
          tar += jmp[p * 2 + ud][i].second;
          tp t = jmp[p * 2 + ud][i].first;
          p = t / 2;
          ud = t % 2;
        }
      }
      tar += abs((ud ? d[p] : u[p]) - yt);
    }();
    bin << tar << '\n';
  }();
  return 0;
}

void MIST() {
}

// :\ */

线段树做法

这题有一个加强版 LOJ 3038,带修改,这样我们的倍增就不管用了。

我们发现这个其实可以合并的。对于一段路来说,有两种可能:撞过墙,或者没撞过墙。如果没撞过墙,我们只需要维护完美的区间(即一直往右也不会撞墙的区间)。如果撞过墙,我们需要维护路径的起点终点和花费。由于撞过墙,于是这个路径是唯一确定的。分类讨论合并。

代码

/* C++17 is required!
 * By Koicy (https://koicy.ly)
 * Email n@rbtr.ee
 * I've reached the end of my fantasy.

         __    __                             __         _
   _____/ /_  / /_________  ___              / /______  (_)______  __
  / ___/ __ \/ __/ ___/ _ \/ _ \   ______   / //_/ __ \/ / ___/ / / /
 / /  / /_/ / /_/ /  /  __/  __/  /_____/  / ,< / /_/ / / /__/ /_/ /
/_/  /_.___/\__/_/   \___/\___/           /_/|_|\____/_/\___/\__, /
                               SIGN                         /___*/
#ifndef XCODE
constexpr bool _CONSOLE = false;
#else
constexpr bool _CONSOLE = true;
#endif
#define __NO_MAIN__ false
#define __ENABLE_RBLIB__ true
constexpr bool _MTS = false, SPC_MTS = false;
constexpr char EFILE[] = "";
#define FULL(arg) arg.begin(), arg.end()
#define LOAD5($1, $2, $3, $4, $5, ...) $5
#define __FOR1(b) for (tp _COUNTER_ = 0; _COUNTER_ < tp(b); ++_COUNTER_)
#define __FOR2(i, b) for (tp i = 0; i < tp(b); ++i)
#define __FOR3(i, a, b) for (tp i = tp(a); i < tp(b); ++i)
#define __FOR4(i, a, b, c) for (tp i = tp(a); i < tp(b); i += tp(c))
#define gor(...) LOAD5(__VA_ARGS__, __FOR4, __FOR3, __FOR2, __FOR1)(__VA_ARGS__)

// :/

signed STRUGGLING([[maybe_unused]] unsigned long TEST_NUMBER) {
  struct S {
    tp l, r, c;
    
    S() : l(-INF), r(INF), c(-1) {};
    S(tp l, tp r, tp c = -1) : l(l), r(r), c(c) {};
    
    static S op(S p, S q) {
      if (p.c == -1 && q.c == -1) {
        if (max(p.l, q.l) <= min(p.r, q.r)) return S(max(p.l, q.l), min(p.r, q.r));
        else {
          if (p.r < q.l) return S(p.r, q.l, q.l - p.r);
          else return S(p.l, q.r, p.l - q.r);
        }
      } else if (p.c == -1) {
        if (p.l <= q.l && q.l <= p.r) return q;
        else {
          if (p.l > q.l) return S(p.l, q.r, q.c + p.l - q.l);
          else return S(p.r, q.r, q.c + q.l - p.r);
        }
      } else if (q.c == -1) {
        if (q.l <= p.r && p.r <= q.r) return p;
        else {
          if (q.l > p.r) return S(p.l, q.l, p.c + q.l - p.r);
          else return S(p.l, q.r, p.c + p.r - q.r);
        }
      } else return S(p.l, q.r, p.c + abs(p.r - q.l) + q.c);
    }
    
    static S e() { return S(); }
  };
  tp n, q; bin >> n >> q; --n;
  vector<tp> u(n), d(n);
  lib::Light_Segtree<S, S::op, S::e> o(n);
  gor(i, n) bin >> u[i] >> d[i];
  gor(i, n) o.set(i, S(u[i], d[i]));
  gor(q) {
    tp op = bin;
    if (op == 1) {
      tp x, u, d; bin >> x >> u >> d;
      o.set(x - 1, S(u, d));
    } else {
      tp xs, ys, xt, yt; bin >> xs >> ys >> xt >> yt;
      if (xs > xt) {
        swap(xs, xt);
        swap(ys, yt);
      }
      S ret = S::op(S::op(S(ys, ys), o.prod(xs - 1, xt - 1)), S(yt, yt));
      bin << xt - xs + max(ZERO, ret.c) << '\n';
    }
  }
  return 0;
}

void MIST() {
}

// :\ */

分治做法

这题有个分治做法,好像还挺好写的,等会学一下。


No comments

Post a comment