CODE FESTIVAL 2018 qual A D - 通勤
これを通せたおかげで予選通過できました。嬉しい。
解法
とします。DPをします。
ガソリンスタンドまで来るにあたって、ガソリンスタンドで最後に補給するとしたときの、そこまでのガソリンスタンドを書店に建て替えるパターン数
次の3つを考える必要があります。
- 近くのガソリンスタンドで補給をしていてガソリンスタンドで補給ができないとき、ガソリンスタンドを書店に建て替えても建て替えなくても変わらない
- やや遠くのガソリンスタンドで補給をしていてガソリンスタンドで補給ができるとき、ガソリンスタンドを書店に建て替えるなら最後に補給した場所は変わらず、建て替えないなら最後に補給したガソリンスタンドはになる
- めっちゃ遠いガソリンスタンドで補給をしていてガソリンスタンドまでたどり着けないとき、ガソリンスタンドまでの情報は無意味になる
まとめると、次のようになります。
これを愚直に処理するととなりTLEします。データを使いまわしてオーダーを落とすことを考えましょう。
列の末尾項を倍し、その前の項の区間和を末尾に追加するという操作ができればいいわけです。
さらに、項は「倍区間」→「区間和区間」→「無意味な区間」と移動する事を利用し、それぞれの区間をqueueで持ち、区間和も持っておけば、遷移が高速化できます。要は尺取法なので、全体の計算量はとなります。
反省
ARC070 Eでやったのと似たようなDPの高速化でした。勉強の成果が出せてよかったです。
この回のC問題と同じく、方針はすんなり立てられたように思います。DPは得意分野にしていきたいです。
そして何より
こどふぇす予選通過してた……!
— ウォンバット (@wombat_packer) 2018年9月26日
本選er各位よろしくです🙇♂️
精進します!
CODE FESTIVAL 2018 qual A C - 半分
解法
になったら、それ以降はで割らないものとします。そうすると、求める答えは本来の「回の操作後の数列としてありうるものの個数」に「回未満の操作後のをつ以上含む数列としてありうるものの個数」を加えたものになります。
からまで考えたときの、回操作してできる数列の個数(はそれまでの要素にがあったかどうかのフラグ)
というDPを考えれば、をで回割るとになるとして、
と遷移が定義できます。あとはここから適切に値をとってきて足し合わせればよいです。
計算量はになります。
反省
DPをDPだと気づくのはけっこう速くなったような気がしますが、詳細を詰めるのが苦手です。
AtCoder Regular Contest 100 F - Colorful Sequences
解法
「全ての列についてのに一致する連続する部分列の個数の総和」から「カラフルでない列についてのに一致する連続する部分列の個数の総和」を引く方針で考えます。前者は簡単なので省略します。後者はがカラフルな場合はなので、以下カラフルでないものとします。
まず次のDPを考えます。
ある特定の長さのdistinctな列の後に、個の要素を繋げてできるカラフルな列の個数
次が成り立ちます。
ただし、このDPを初期化するには、が分かっていないといけません。そこで、
ある特定の項の後に、個の要素を繋げてできるカラフルな列であって、distinctなsuffixの最大長さがであるものの数
というものを考えれば、
と遷移が定義できることがわかります。です。これらのDPはいずれも累積和で高速化できます。
さて、以上を使って「カラフルでない列についてのに一致する連続する部分列の個数の総和」を求めていきましょう。
がdistinctな場合。数列中に長さのdistinctな列が登場する場合の数は、終了位置をと固定すれば、です。その中に、はちょうどの割合で存在します。あとはをからまで動かして総和をとればよいです。
がdistinctでない場合。のdistinctなprefixの最大長さを、suffixの最大長さをとすると、数列中にが登場する場合の数は、開始位置をと固定すれば、になります。あとはをからまで動かして総和をとればよいです。
計算量は全体でになります。
反省
5日かかりました。難しかった。
コンテスト中のACが少ない問題はなかなか解けなくても自己嫌悪に陥らないので良いです。(にしても5日はかかりすぎか)
の遷移を立てるまでにかなりの苦労がありました。しかも蓋を開けてみたら非想定解でした。想定解の方が数倍賢い。
AtCoder Regular Contest 102 F - Revenge of BBuBBBlesort!
自力AC最高点を更新しました!
解法
まず、操作の性質を考えてみると、以下のようなものが浮かび上がります。
- 一度反転した3項の中央は、その後動かすことはできない
- 一度動かした項を元の位置に戻すことはできない
一つ目を示します。項目を中心に反転させたとします。ここから項目を動かすには、項目を中心に反転させる必要がありますが、そのままでは項目と項目が反転できる大小関係を満たしません。そこで項目を中心に反転させることでこれを解消しても、今度は項目と項目が反転できる大小関係を満たさなくなります。
二つ目は、一つ目の派生です。一度壊した大小関係は元には戻せません。(人間関係と似ていますね)
一つ目の性質から、その後動かさなくていい項、つまりなる項目しか反転の中心にできないことがわかります。二つ目の性質から、このような項はむしろ動かしてはなりません。このような項をピボットと呼ぶことにします。さて、ピボットは操作の過程で増えるでしょうか? たしかに条件を満たす項は増えていきますが、実際にそこを中心とした反転を行うことはできない(動かすことのできない項が必ず隣にある)ため、ピボットは増えないものとしてよいことがわかります。よって、入力は以下を満たす必要があります。
- 任意のについて、項目のいずれかはピボットである
ここで、項を偶数番目と奇数番目に分けたとき、ピボットを区切りとした連続する非ピボット同士で数をやり取りするしかないため、そのような項のみ取り出します。まず、それらが集合として正しくない場合は弾きます。次にそれらを昇順に並べることを考えます。以下のような必要条件が浮かびます。
つまり、ある2つの項を入れ替えなくてはならないとき、その間にそれらを入れ替えられるピボットが必要という事です。
実はこれは十分条件でもあります。つまり、条件を満たした状態から反転を行っても依然として条件は満たされたままになっています。例えば(がピボット)のような列において、を中心に反転を行うととが交換できなくなるように思えますが、よく考えるとより右側により小さい項が1つはあるはずであり、元から条件を満たしていません。反転後に条件を満たさなくなるように思えるのはすべてこのパターンであることがわかります。
では、条件を満たしているかどうかを判定する方法を考えましょう。区間と区間が共通部分を持たないとすると、かであるといえます。(非ピボットなので等しいことはない)
よって、非ピボットな項目について、ならばそれより右にそれより小さい非ピボットがなく、ならばそれより左にそれより大きい非ピボットがないとき、条件は満たされています。これは、左からの累積列と右からの累積列を持っておけば、全体としてで判定することができます。
反省
初めてARC-Fなるものを自力で通し、根気よく考察を進めていけば案外解けなくもないことがわかりました。
思考過程はほぼ解法に書いた通りですが、「項を偶奇で分けてピボットで区切った非ピボットたちが集合として正しくない場合は弾く」あたりが少しだけ違います。(といっても、予めそういうのが弾かれるような条件を考えてあったというだけです(ACコードはそっちの実装になっています))
AtCoder Regular Contest 102 D - All Your Paths are Different Lengths
これは流石に解きたかった
解法
- 頂点から頂点に長さの辺と長さの辺を張る
とすると、経路長からのパスが1本ずつできます。
まずはこれでパスの最大長さをを超えない最大まで持って行きましょう。そこから、
- 頂点から頂点に長さ の辺を張る
とすると、パスの最大長さがだけ更新されることがわかります。したがって、の二進数における各桁を取り出しながら、適切に辺を張り最大長さを更新していけばよいです。
AtCoder Regular Contest 070 E - NarrowRectangles
考察してて生えてきた図に既視感があると思ったら、以前諦めて解説だけ見た問題だった
解法
というDPについて考えます。以下が成り立ちます。
ここまでで部分点が取れます。
実はは、傾きが段階的に変化するU字状の関数になっており、傾きが変わる点の集合をとして管理することができます。具体的には、
- 傾きのところから点を左右に分け、左側を降順、右側を昇順のpriority_queueとして持つ。左右それぞれで点同士の相対位置が正しくなるようにする。傾きの線の両端の点だけ正しい座標を持っておく。
- 漸化式の第1項を処理することを考える。傾きの線が広がることがわかるので、両端の点の座標を更新する。
- 漸化式の第2項を処理することを考える。ある点で傾きが変わることがわかるので、priority_queueに同じ点を2つ追加し、左右間で点を受け渡す。座標と答えを更新する。
とすればよいです。
計算量はとなり、満点が得られます。
反省
解説だけ見たものも意外と覚えているらしく、割とすんなりACできました。
データ構造を持ってDPのオーダーを落とすというのは初めてやったように思います。こういうのもあると心の片隅に置いておきます。
AtCoder Regular Contest 066 E - Addition and Subtraction Hard
過去問。
なんかやけにあっさり解けて、順位表見たら通してる人が意外と少なく、ついに僕も覚醒したか? と思ったらXor Sum回だった
解法
正符号の後ろから括弧を開始するのは明らかに無意味です。ある負符号から何項かを括弧で括ることを考えます。(この括弧を外括弧と呼ぶことにします)
ここで、外括弧内の項はなるべく最終的に正にしてやりたいです。
このように負符号を区切りとして括るようにすると、与式において正符号が続く最初の何項かを除いて全部正にできることがわかります。
この最初の何項かというのは、外括弧の開始位置を固定して意味のある括り方をしようとすると必ず巻き込まれてしまいます。(必要な犠牲)
外括弧内の後ろの方は正にできるので、終了位置は与式の一番最後にするとよさそうです。外括弧は1つでよいこともわかります。
最終的に負となってしまうのは、開始位置からの何項かと、開始位置より前にあるもとから負だった項です。
ここまでわかれば、あとは与式を前から見ていって開始位置を動かしながら、なるべく大きくなるような答えを探せばよいです。
反省
正の項しかない場合の処理を忘れて1WA食らったものの、ほとんど解法に書いた道筋通りに考察を進め、スムーズに解くことができました。
コンテスト本番でもかくありたいものです。