F - Falling Bars Editorial
by
yuto1115
解説
以下、\(R_1 \geq R_2\geq \dots \geq R_N\) を仮定します。
まず、バー \(i\) の最終位置を求める上で、バー \(j\ (j > i)\) について考える必要はありません(初期状態でバー \(i\) より上にあるバーが途中でバー \(i\) の下に来ることはないため)。よって、\(i=1,2,\dots,N\) の順に最終的なバー \(i\) の位置を求めていくことを考えられます。
ある \(a\ (1\leq a\leq N)\) に対し、バー \(i\ (i < a)\) の最終位置がすべて求まっているときにバー \(a\) の最終位置を求める方法について考えます。
各 \(1\leq j\leq W\) に対し、\(m_j\) を「バー \(1,2,\dots,a-1\) のみが存在している状態で十分な時間が経過したとき、何らかのバーに占められているような \(j\) 列目のマスのうち最も上にあるものの行番号(\(j\) 列目のどのマスも占められていなければ \(H+1\))」と定義します。
このとき、十分な時間が経過した際にバー \(a\) が位置する行 \(R'_a\) は、\(\min\{m_j: C_a \leq j < C_a+L_a\}-1\) と一致することが示せます。
バー \(a\) の最終位置を求めたあとは、次にバー \(a+1,a+2,\dots\) の最終位置を求めるため、\(m_j\) の値を適切に更新する必要があります。具体的には、各 \(j\ (C_a \leq j < C_a+L_a)\) に対し、\(m_j\) の値を \(R'_a\) で更新する必要があります。
まとめると、以下のような操作を高速に処理できれば良いです。
- \(m_1=m_2=\dots=m_W=H+1\) と初期化する。
- \(i=1,2,\dots,N\) の順に以下を行う:
- \(R'_i\leftarrow \min\{m_j: C_i \leq j < C_i+L_i\}-1\) とする
- 各 \(j\ (C_i \leq j < C_i+L_i)\) に対し、\(m_j\leftarrow R'_i\) とする
これは、区間 min・区間 update (あるいは区間 min・区間 chmin)の遅延セグメント木を用いることで、全体で \(O(W+N\log W)\) で処理することができます。よって本問題を解くことができました。
実装例 (C++) :
#include <bits/stdc++.h>
#include <atcoder/lazysegtree>
using namespace std;
using namespace atcoder;
const int inf = 1e9;
using S = int;
using F = int;
S op(S l, S r) { return min(l, r); }
S e() { return inf; }
S mapping(F l, S r) { return min(l, r); }
F composition(F l, F r) { return min(l, r); }
F id() { return inf; }
int main() {
int h, w, n;
cin >> h >> w >> n;
vector<int> r(n), c(n), l(n);
for (int i = 0; i < n; i++) {
cin >> r[i] >> c[i] >> l[i];
--c[i];
}
vector<int> ord(n);
iota(ord.begin(), ord.end(), 0);
sort(ord.begin(), ord.end(), [&](int i, int j) { return r[i] > r[j]; });
vector<int> ans(n);
lazy_segtree<S, op, e, F, mapping, composition, id> seg(vector<int>(w, h + 1));
for (int i: ord) {
ans[i] = seg.prod(c[i], c[i] + l[i]) - 1;
seg.apply(c[i], c[i] + l[i], ans[i]);
}
for (int i = 0; i < n; i++) {
cout << ans[i] << '\n';
}
}
posted:
last update: