Official

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\) で更新する必要があります。

まとめると、以下のような操作を高速に処理できれば良いです。

  1. \(m_1=m_2=\dots=m_W=H+1\) と初期化する。
  2. \(i=1,2,\dots,N\) の順に以下を行う:
    1. \(R'_i\leftarrow \min\{m_j: C_i \leq j < C_i+L_i\}-1\) とする
    2. \(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: