geam1113’s diary

主にAtcoderのコンテストの備忘録を書きます。AtCoder緑と水色を行ったり来たりしています。

ABC245 参加記録

コンテスト中AC:A〜C
コンテスト後解説なしでE,FをAC。
Dは解説見たが、方針は間違っていなかったが、考慮できていない部分があった。

D - Polynomial division

C_i = A_0 B_i + A_1 B_{i-1}+\cdots +A_i B_0

であるので、

i=0の時、B_0 = \frac{C_0}{A_0}

i\geq 1の時、B_i = \frac{C_i - (A_1 B_{i-1}+\cdots +A_i B_0)}{A_0}

として、B_0, B_1,\cdots ,B_Mの順で求められると考えました。
(なお、i\gt Nの時はA_i=0としておけば良い)

しかしながら、これはREになっていました。
なぜかというと、A_0 = 0の場合があるからでした。

そこで、以下のように考えました。
A_i \neq 0となる最小のiposとします。

C_{pos} = A_{pos} B_{0}

C_{pos+1} = A_{pos+1} B_{0} + A_{pos} B_{1}

C_{pos+2} = A_{pos+2} B_{0} + A_{pos+1} B_{1} + A_{pos} B_{0}

\cdots

となるので、以下の方針としました。

i=posの時、B_0 = \frac{C_0}{A_{pos}}
i\geq pos+1の時、B_{pos-i} = \frac{C_{i} - (A_{i} B_{i-1}+\cdots +A_{pos} B_0)}{A_{pos}}

とします。 しかし、この解法でもREとなり、時間内にACできませんでした。

コンテスト後に考えた結果、実装時に色々と考慮できていなかった部分があったことが原因であり、解法自体は正しいことがわかりました。

以下がACしたコードです。

提出コード

上の実装に、n = max(N,M)として、A,B,Cについて、それぞれ2n-1個分の領域を確保する処理がありますが、ここは不要です。

全てN+M個分確保しておけば良いです。逆にこれより少なくとだめです。
なぜなら、A_N\neq 0のみが保証されているため、iは最大でN+Mになるからです。

また、今回、Aのインデックスをベースに実装しましたが、Bをベースにした方がわかりやすいと思います。
提出コード

E - Wrapping Chocolate

小さいチョコレートから優先して箱に入れる場合、大きい箱に入れると、後々入れられないチョコレートが出てくる可能性があります。そこで、チョコレートを大きいものから順に入れることを考えます。

ここでチョコレートの大小関係を定義しておきます。チョコレートを縦を第一キー、横を第二キーとして降順ソートしたときの基準を適用します。すなわち、i番目のj番目のチョコレートについて、

  • A_j \lt A_iの場合はi番目が大きく、A_j = A_iの場合はB_j \lt B_iならi番目が大きい

となります。

まだ箱に入れられていないチョコレートの中で最大のものをi番目とします。

まず、縦を考えます。
A_i \leq C_jを満たす箱jに入れることが可能です。ここで、これを満たす箱であれば、どれに入れても、縦の制限によって箱に入れられないチョコレートが発生することはありません。なぜなら、残ったチョコレートの縦はA_i以下だからです。

次に横を考えます。
横については、B_i \leq D_jを満たす箱jならどれでもよいというわけにはいきません。なぜなら、残ったチョコレートの横はB_i以下であるとは限らないからです。
ということから、横はB_i \leq D_jを満たす中で最小のものにしておく必要があります。

以上をまとめると、以下のような貪欲法になります。

1.箱に入っていないチョコレートの中で最大をものを1つ選びチョコレートiとする
2.チョコレートが入っておらず、かつ、チョコレートiが入れられる箱の中で、横が最小の箱jを選ぶ
3.チョコレートiを箱jに入れる

具体的はアルゴリズムに移ります。

順序付き多重集合Sにチョコレートiを入れる候補となる箱を記録することにします。Sの順序はDの昇順とします。

Sには、チョコレートiを入れたい時、A_i \leq C_jを満たす箱jを全て記録します。その中から、B_i \leq D_kを満たす中で最小の箱kを二分探索によって探し、Sから削除します。

なお、Sに入っている箱は全てA_i \leq C_jを満たすので、C_jは記録する必要がなく、D_jだけ記録しておけばよいです。

これを素直に実装すると、各チョコレートについて、候補となる箱を探すため、集合への追加を除いてもO(NM)となり、間に合いません。

そこで以下の方法とします。

まず、チョコレートと箱はそれぞれ縦(A_i,C_i)を第一キーに、横(B_i,D_i)を第二キーとして降順ソートします。

i\lt i'の時、A_{i'} \leq A_{i}なので、A_i \leq C_jである時、A_{i'} \leq C_j となります。

そのため、チョコレートiSに追加した箱は、チョコレートi'でも追加したままにしておけます。

従って、i=1,2,\cdots ,Nの順に調べる時、 チョコレートiSに追加された箱は、チョコレートi+1でも追加したままにでき、チョコレートi+1で新たに必要になった箱だけをSに追加すればよくなります。

これにより、箱はSに高々1回しか追加されず、全体でO(N)となります。

多重集合としてC++のmultisetを使えば、追加、検索、削除はlog時間でできるので、高速に実現できます。

提出コード

F - Endless Walk

頂点vを出発して、頂点vに帰って来れるような閉路が存在すれば、vに到達後に無限に移動できることが確定します。

ある強連結成分C_i = \{v_{i_1},v_{i_2},\cdots v_{i_m}\}に属する全ての頂点は、この条件を満たします。

よって、強連結成分の集合族をC=\{C_1,C_2,...,C_m\}としたとき、これら強連結成分に属するいずれかの頂点に到達できれば、無限に移動できます。

強連結成分は、強連結成分分解でO(N+M)で得られます。AtCoder Libraryのscc_graphを強連結成分が二次元配列で得られるので、列のサイズが2以上であれば閉路を含み、C_iとみなせます。

(補足:グラフが単純でなければ、自己ループを考慮する必要があるので、サイズが1でも閉路となります。)

あとは、強連結成分の頂点に到達可能な頂点を調べれば良いですが、以下のように言い換えました。

  • Gの有向辺を全て逆向きにしたグラフG'において、強連結成分のいずれかの頂点から到達可能な頂点を調べる

これは強連結成分に属する全ての頂点をキューに入れてBFSを開始すれば良いです。

提出コード