TIOJ 2200:導火面板改(Fire)

TIOJ 2200導火面板改(Fire)


題目大意:有一個範圍 $(0, 0)\sim (R, C)$ 的面板,有 $N$ 個起火點 $(x_1, y_1)\sim (x_N, y_N)$,每個起火點開始起火的時間在 $t_i$,火會向四面八方蔓延,實數點 $(x, y)$ 在時刻 $t$ 有被火燒到需要存在至少一個 $i$ 使得 $|x_i - x| + |y_i - y| \leq t - t_i$,請你輸出最早的整數秒使得整個面板都起火。

解法:可以二分搜時間,然後可以座標轉換,這樣就會變成很多矩形,問你是不是所有點都被矩形覆蓋到,然後就可以線段樹掃描線。對每個 $x$ 座標所組成的區間 $[u, d]$ 我們要詢問的就是這個範圍內 $y$ 座標可以到達最小的值與最大的值。

$\text{Code:}$

#pragma GCC optimize("O3")
#pragma GCC target("popcnt")
#include <bits/stdc++.h>
using namespace std;

struct Lis : vector<int> {
  void deal() {
    sort (begin(), end());
    erase (unique (begin(), end()), end());
  }
  int operator () (int val) {
    return lower_bound (begin(), end(), val) - begin();
  }
};

const int SIZE = 5e4 + 5;
const int TSIZ = 6 * SIZE;

int N, R, C;
int n, m;
int t[SIZE], x[SIZE], y[SIZE];
int u[SIZE], d[SIZE], l[SIZE], r[SIZE];
int ql[TSIZ], qr[TSIZ];
int mn[4 * TSIZ], lazy[4 * TSIZ];
vector<tuple<int, int, int>> op[TSIZ];

void build (int pos, int l, int r) {
  mn[pos] = lazy[pos] = 0;
  if (l < r) {
    int mid = (l + r) / 2;
    build (pos << 1, l, mid);
    build (pos << 1 | 1, mid + 1, r);
  }
}

void Push (int pos, int l, int r) {
  mn[pos] += lazy[pos];
  if (l < r) {
    lazy[pos << 1] += lazy[pos];
    lazy[pos << 1 | 1] += lazy[pos];
  }
  lazy[pos] = 0;
}

void Pull (int pos, int l, int r) {
  int mid = (l + r) / 2;
  Push (pos << 1, l, mid);
  Push (pos << 1 | 1, mid + 1, r);
  mn[pos] = min (mn[pos << 1], mn[pos << 1 | 1]);
}

void Upd (int pos, int l, int r, int L, int R, int delta) {
  if (l == L && r == R) {
    lazy[pos] += delta;
    return;
  }
  Push (pos, L, R);
  int mid = (L + R) / 2;
  if (r <= mid) {
    Upd (pos << 1, l, r, L, mid, delta);
  } else if (l > mid) {
    Upd (pos << 1 | 1, l, r, mid + 1, R, delta);
  } else {
    Upd (pos << 1, l, mid, L, mid, delta);
    Upd (pos << 1 | 1, mid + 1, r, mid + 1, R, delta);
  }
  Pull (pos, L, R);
}

int query (int pos, int l, int r, int L, int R) {
  Push (pos, L, R);
  if (l == L && r == R) {
    return mn[pos];
  }
  int mid = (L + R) / 2;
  if (r <= mid) {
    return query (pos << 1, l, r, L, mid);
  } else if (l > mid) {
    return query (pos << 1 | 1, l, r, mid + 1, R);
  } else {
    return min (query (pos << 1, l, mid, L, mid), query (pos << 1 | 1, mid + 1, r, mid + 1, R));
  }
}

pair<int, int> cal (int l, int r) {
  int rl = (r <= C ? -r : l >= C ? l - 2 * C : -C);
  int rr = (r <= R ? r : l >= R ? 2 * R - l : R);
  return {rl, rr};
}

bool ok (int qt) {
  Lis lx, ly;
  for (int i = 1; i <= N; i++) {
    if (qt <= t[i]) {
      continue;
    }
    int dt = qt - t[i];
    u[i] = max (0ll, 1ll * x[i] - dt);
    d[i] = min (1ll * (R + C), 1ll * x[i] + dt);
    l[i] = max (-1ll * C, 1ll * y[i] - dt);
    r[i] = min (1ll * R, 1ll * y[i] + dt);
    lx.emplace_back (u[i]);
    lx.emplace_back (d[i]);
    ly.emplace_back (l[i]);
    ly.emplace_back (r[i]);
  }
  lx.deal(), n = lx.size();
  if (n == 0 || lx[0] != 0 || lx.back() != R + C) {
    return 0;
  }
  fill (op, op + n, vector<tuple<int, int, int>>());
  for (int i = 0; i < n - 1; i++) {
    tie (ql[i], qr[i]) = cal (lx[i], lx[i + 1]);
    ly.emplace_back (ql[i]);
    ly.emplace_back (qr[i]);
  }
  ly.deal(), m = ly.size();
  for (int i = 1; i <= N; i++) {
    if (qt <= t[i]) {
      continue;
    }
    u[i] = lx (u[i]), d[i] = lx (d[i]);
    l[i] = ly (l[i]), r[i] = ly (r[i]);
    r[i]--;
    op[u[i]].emplace_back (l[i], r[i], 1);
    op[d[i]].emplace_back (l[i], r[i], -1);
  }
  build (1, 0, m - 1);
  for (int i = 0; i < n - 1; i++) {
    for (auto [l, r, delta] : op[i]) {
      Upd (1, l, r, 0, m - 1, delta);
    }
    ql[i] = ly (ql[i]);
    qr[i] = ly (qr[i]) - 1;
    if (ql[i] <= qr[i] && query (1, ql[i], qr[i], 0, m - 1) == 0) {
      return 0;
    }
  }
  return 1;
}

int main() {
  ios::sync_with_stdio (false), cin.tie (0);
  cin >> N >> R >> C;
  for (int i = 1; i <= N; i++) {
    int dx, dy;
    cin >> t[i] >> dx >> dy;
    x[i] = dx + dy;
    y[i] = dx - dy;
  }
  int tm = *min_element (t + 1, t + N + 1);
  for (int i = 1; i <= N; i++) {
    t[i] -= tm;
  }
  int l = 0, r = R + C;
  while (l < r) {
    int mid = l + (r - l) / 2;
    if (ok (mid)) {
      r = mid;
    } else {
      l = mid + 1;
    }
  }
  cout << 1ll * tm + l << '\n';
}

我的分享就到這裡結束了,如果喜歡我的 $\text{Blog}$,歡迎追蹤!

留言

熱門文章