−目次
トヨタ自動車プログラミングコンテスト2024#11(AtCoder Beginner Contest 379) F,G 問題メモ
C問題に誤読しやすい罠があり、その罠に気付かせるような入力例も用意されていなかったため、序盤の問題にしては結構な人がWAしていた。
ABC-Cのくせに よわよわサンプル なさけな~い♥ ざあこ♥ざあこ♥
F - Buildings 2
問題文
- ビル 1, ビル 2, …, ビル N の N 棟がこの順で東西に一列に並んでおり、ビル 1 が最も西に、ビル N が最も東に建っています。ビル i (1≤i≤N) の高さは Hi です。
- 整数の組 (i,j) (1≤i<j≤N) に対して、以下の条件を満たすとき ビル i からビル j を見ることができます。
- ビル i とビル j の間にビル j より高いビルが存在しない。すなわち、Hk>Hj を満たす整数 k (i<k<j) が存在しない。
- Q 個の質問に答えてください。i 番目の質問では整数の組 (li,ri) (li<ri) が与えられるので、ビル ri より東にあるビル(ビル ri+1, ビル ri+2,…,ビル N )のうちビル li とビル ri の両方から見ることができるものの個数を答えてください。
制約
- 2≤N≤2×105
- 1≤Q≤2×105
- 1≤Hi≤N
- Hi≠Hj (i≠j)
- 1≤li<ri≤N
- 入力は全て整数
解法
数列 A に対し、先頭から見ていってこれまでに出てきた最大値を更新した値だけを抽出した数列を f(A) とする。
A : 3 1 2 5 4 6 ↓ f(A): 3 5 6
まず r から見えるビルだけに絞って考える。そのようなビル群(の高さ)は、以下のようになる。
- B=f((Hr+1,Hr+2,...,HN))
r から見えないビルが l から見えることは無いが、逆に r から見えるビルが l から見えないことはある。
max(これを T とする)より高くないと、l からは見えない。
B に出てくる、T 以上の値の数が答えとなる。
B は単調増加なので、二分探索して T 以上となる境界を求められる。
T はセグメント木やSparceTableなどを利用して求められる。
B は、全クエリを通して、末尾からスタックを利用して構築できる。
k=N,N-1,...,1 の順に B を更新し、各クエリは r_i=k となったタイミングで処理すればよい。
オンライン解法
クエリを先読みしなくても解く方法がある。
G - Count Grid 3-coloring
問題文
- 1,2,3,? からなる H 行 W 列のグリッド S が与えられます。上から i 行目、左から j 列目の文字は S_{i,j} です。
- S の各 ? を 1,2,3 のいずれかに置き換えて得られるグリッドは ? の個数を q として 3^q 通りありますが、そのうち以下の条件を満たすものはいくつありますか? 998244353 で割った余りを出力してください。
- 隣接する(辺を共有する)どの 2 つのマスにも異なる数字が書かれている。
制約
- 1\leq H,W
- H\times W\leq 200
- H,W は整数
- S は 1,2,3,? からなる H 行 W 列のグリッド
解法
H \times W が小さい。
14^2=196 なので、短い方は 14 以下であることが保証されている。
必要に応じてグリッドを転置させて、H \ge W である(W \le 14 である)状態にしておく。
左上から1要素ずつ、1/2/3 の何を置いたかを状態に持ったDPで決めていく。
「(i,j) に置いた数字は上・左に置かれた数字(存在すれば)のいずれとも異なる」ことを、「(i,j) は条件を満たす」と呼ぶことにする。
- \mathrm{DP}[i,j,S]:= 下記のような状態で、□■は全て条件を満たしていて、■にそれぞれ置いた数字の列が S である場合の数
j □□□□□□ □□□□□□ ■は、(i-1,j+1) から (i,j) までの W 個のマスを示す □□■■■■ i ■■
(i,j) に条件を満たしつつ何の数字を置けるかは、\mathrm{DP}[i,j-1,S] からそれぞれの S につき、1つ上と1つ左に置いた数字の情報があるので決められる。
その他の■は (i,j) を決めるのには直接参照しないものの、次の (i,j+1) 以降の更新では参照されるため、必要となる。
S の状態数は 3^2 \times 2^{W-2} なので、全体 O(HW \times 3^2 \times 2^{W-2})。 制約の代入で 7.4 \times 10^5 程度と見積もられる。
Python, PyPyでは、S をそのまま列で持つ(辞書のキーにタプルを用いる)と遅くなるし、遷移時に S をスライドさせる処理も O(W) 余分にかかるため、TLEになりやすい。
1マスに2bitずつ用いて、2W bitの整数で持たせると、辞書アクセスが速くなり、スライド処理も bitshift で高速化できる。