コンテストページ:AtCoder Beginner Contest 285 - AtCoder
コンテスト中AC:A〜D
コンテスト後自力AC:E,F
D - Change Usernames
ユーザ名の重複を取り除き、番号を振り直したものをとします。
から
に、
から
に変更することを考えます。
配列の途中に要素を挿入する場合に末尾要素から順に後ろにずらしてスペースを作りますが、同じように、を
に変更してから
を
に変更すれば、2つの変更を達成できます。
達成できない場合を考えると、例えば、から
に、
から
に、
から
にする変更は不可能です。
これらの関係を有向グラフの問題にすると見通しが良くなります。
の各要素を頂点、
から
に変更したい場合に
を始点、
を終点として有向辺を張ります。ユーザ名の変更は、有向辺を一回辿ることと考えることができます。
この有向グラフに閉路があると、移動順を決められず、達成できないことがわかります。逆に閉路がない場合、トポロジカルソートして、末尾から順に移動すれば達成できます。
従って、このグラフに閉路存在するかを判定すればよいです。
コンテストでは、AtCoder Libraryの強連結成分分解を使いました。
実装: Submission #38053494 - AtCoder Beginner Contest 285
E - Work or Rest
連勤する日数に対して、得られる生産量は固定です。
例えば3連勤すればが生産量です。
これは、の累積和をとっておき、連勤する日数の偶奇で場合分けすることで
で計算できます。
この問題では、以下のように考えることができます。
連勤に必要な日数を
、
連勤で得られる生産量を
とする。合計日数が
以下となるように選んで連勤する時、得られる生産量の最大値を求めよ。但し、各連勤は何度してもよい。
これは個数制限の無いナップサック問題と同じです。連勤日数は最大で日なので、
で解けます。
注意が必要なのは、連勤に必要な日数は
日ではないということです。
前か後ろの一方にのみに休日を入れておけば全体として矛盾がないので、日としておけば良いです。
実装: Submission #38131229 - AtCoder Beginner Contest 285
F - Substring of Sorted String
を、
と表現します。
が
の連続する部分列であるための条件は以下の2つです。
が昇順に並んでいる
- 文字をabc順に並べた時に
より後ろ、かつ、
より前の文字
について、
に含まれる
の個数と
全体に含まれる
の個数が等しい
1つ目は自明だと思うので、2つ目を具体例で示します。
とします。
まず、は
の先頭と末尾ですが、これは
に何個含まれていても良いです。例えば、
の連続する部分列が、
であったとしても、
という感じに分けることを考えればに一致させることができることがわかります。
反対にと
の間の文字、
は
に含まれる個数と
全体に含まれる個数が一致しなければいけません。
もし、以外の箇所にこれらの文字が含まれていた場合、昇順ソートして
にした時に、
の間に1文字以上挿入されることになり、一致しなくなります。
次に、これらの計算方法です。
まず、昇順に並んでるかどうかですが、これは順序付き集合で、以下を管理します。
の連続する部分列であって、その部分列が昇順であるように(最長に)分割した時の、各部分列の開始位置
例えば、は、
と分割できるので、
となります。
が与えられたとき、
上を二分探索し、
及び
より大きい最小の整数(upper_bound)が一致すれば、
は昇順です(実装では、
を
に入れておけば扱いやすいです)。
文字変更クエリでは、の中身を変更していきます。変更される可能性があるのは
の2つです。文字変更によって、新たに昇順になったか、または昇順でなくなったか、に応じて、
から
を追加または削除すればよいです。
値の追加、削除、二分探索が可能なデータ構造(C++ならset)を用いれば各クエリでで処理できます。
次に、2つ目の条件ですが、これは値の1点更新を伴う区間和を処理できればよいので、Fenwick Treeで対応できます。
なお、2個目のクエリで全ての文字について、その個数を求めたとしても、文字種が26しかないので計算量としては問題ないです。