AtCoder Beginner Contest 216 (ABC216) G - 01Sequence の解法です. 公式解説 によると 「牛ゲー 」で解けるとのことですが,別の (より効率が悪い) 方法で解きました.
問題へのリンク
https://atcoder.jp/contests/abc216/tasks/abc216_g
問題概要
$N$ と $(L_i, R_i, X_i)$ ($i = 1, \ldots , M$) が与えられる. 長さ$N$の0と1からなる数列 $(A_i \mid i \in [1, N])$ で, 条件 $ | \{ i \in [L_i, R_i] \mid A_i = 1 \}| \geq X_i $ を満たすもののうち, 1の数が最小のものを一つ作れ.
制約: $ N, M \leq 2 \times 10^5 $
解法
0の数の条件に書き換える.$Y_i := R_i - L_i + 1 - X_i$ として, 条件 $ | \{ i \in [L_i, R_i] \mid A_i = 0 \}| \leq Y_i $ のもとで, 全体の0の数を最大にする.
$S_j := \{ i \in [1, M] \mid L_i \leq j \leq R_i \}$ と書く.
$A_0, A_1, \ldots$ の順に $A_i$ の値を,貪欲に 0 を 置くように決めていくことができる.理由は以下の通り. 今,$A$ が条件を満たすとする.$i \in S_j$ に対し,
$ t(i, j) := | \{ k \in [L_i, j) \mid A_k = 0 \} |$
と書く.ある $j\in [1, N]$ について,$A_j = 1$ であり,しかも すべての $i \in S_j$ について $t(i, j) < Y_i$ であったとする.この次に$A$の値が0となる添字を $k$ とする, すなわち,$A_{k} = 0$ で,すべての $l \in [j, k)$ に対して $A_l = 1$ とする.このとき,$A_j$ と $A_{k}$ の 0/1 を入れ替えた列も,条件を満たす. そのような $k$ が存在しないときは,単に $A_j$ の値を 0 にした列が 条件を満たす.
したがって,次の構成を行えば良い: $A_0, \ldots, A_{j-1}$ まで決めたとして,$A_j$ を定める.
- すべての $i \in S_j$ について,$t(i, j) < Y_i$ のとき,$A_j = 0$
- そうでないとき,$A_j = 1$
イベントソートを用いて,次のようにすれば良いように考えられる.
- 初期時点 $j = -1$ では,すべての $i$ に対し,その残り容量は $\infty$ である.
- 時点$j$ において,
- $j = L_i$ となる $i$ の残り容量を $Y_i$ に設定する.
- $j = R_i$ となる $i$ の残り容量を $\infty$ に設定する.
- 残り容量が 0 である $i$ がある場合,
- $A_i := 1$ と定める.
- 残り容量が 0 である $i$ がない場合,
- $A_i := 0$ と定める.
- 全部の $i$ の残り容量を 1 減らす.
しかし,全部の残り容量を愚直に減らしていては間に合わない. 代わりに, 「自分の区間が終わるまでに,0の累積個数 $n$ がいくつ以下でなければならない」 という量 $Z_i$ を持つことにする.
- 初期時点 $j = -1$
- すべての $i$ に対し,$ Z_i := \infty $
- $n := 0$
- 時点$j$ において,
- $j = L_i$ となる $i$ に対し,$ Z_i := n + Y_i $
- $j = R_i$ となる $i$ に対し,$ Z_i := \infty $
- $Z_i$ たちの最小値が $n$ である場合:
- $A_i := 1$
- $Z_i$ たちの最小値が $n$ より大きい場合:
- $A_i := 0$
- $n := n + 1$
これは,$Z_i$ を優先度キューで管理することで実装できる.