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}$,歡迎追蹤!
留言
張貼留言