Educational Codeforces Round 52 参加記録&解説(A~F)
Dashboard - Educational Codeforces Round 52 (Rated for Div. 2) - Codeforces
結果は4完で194位。DがHackで落ちました…
A~Fの6問を振り返ります。
A. Vasya and Chocolate
問題概要
以下の問題 個に答えよ。
制約
解法
買えるチョコは 個です。これを 個1組とすると 組買っているので、追加でもらえるチョコは 個です。 が答えです。
ACコード
(C++)Submission #44119682 - Codeforces
B. Vasya and Isolated Vertices
問題概要
頂点 辺の単純な無向グラフの中で、孤立点が最も少ないグラフと、最も多いグラフを考える。それぞれの孤立点の数を求めよ。
制約
解法
孤立点を最も少なくするには、孤立点同士を辺で結んでいくのがよいです。1本の辺につき孤立点が2個減るので、孤立点の最小数は となります。
孤立点を多くするには、なるべく少ない頂点だけで全部の辺を持ちたくなります。そのため、完全グラフを作るように辺を張っていくとよいです。 頂点の完全無向グラフの辺数は 本なので、 であれば 頂点で 本の辺を持てます。この不等式を満たす最小の を求めて、孤立点の最大数は となります。
ACコード
Submission #44123483 - Codeforces
C. Make It Equal
問題概要
ブロックが積まれてできた 本の塔があり、それぞれの高さはブロック 個である。
以下の操作を、全ての塔の高さが等しくなるまで繰り返す。操作回数の最小値を求めよ。
- ある高さを決める。このとき、その高さ以上にあるブロックの個数が 個以下でなければならない。その高さ以上のブロックを全て取り除く。
制約
解法
毎回の操作で、 個を超えないギリギリまでブロックを取り除くのがもちろん最適です。そのため、上から数えたブロック数が 個を超えない最小の高さを求めたくなります。
「今の操作で除去できる残りブロック数」を管理しておいて、今の操作でどこまで除去できるか1段ずつ調べます。このとき「今の時点で一番高さが高い塔の本数」を管理しておくと、1段下げたときいくつのブロックが除去されるかが分かります。
残りブロック数が足りなくなった時点で、次の操作に移ります。これを一番低い塔の高さに到達するまで繰り返します。
の上限が少ないので、これで十分間に合います。
ACコード
(C++)Submission #44183329 - Codeforces
本番ではもう少しまとめて計算するようなコードを書いていて、こっちだと が大きくても対応できるのですが、そのぶん実装が少しだけ複雑です。
D. Three Pieces
問題概要
のチェス盤の各マスに、 の数字が1つずつ記載されている。このチェス盤上で、以下の操作を行う。
- 1のマスに、ルーク・ビショップ・ナイトのうち好きな種類の駒を置く。
- 以下の操作を好きな順番で繰り返し、 の順にマスを訪れる。途中で他のマスを経由しても良い。
- チェスのルールにおける駒の動き方に則って駒を1手分動かす。
- 駒をルーク・ビショップ・ナイトのいずれかに交換する。
が書かれたマスに到達するまでの操作の合計回数を 、そのうち駒の種類を交換した回数を として、 を辞書順最小にしたい。そのような を求めよ。
制約
- マス に書かれた数を として、 でありこれらは互いに異なる
解法
駒の交換と移動があり、遷移が複雑です。グラフに帰着して、最短路問題などのアルゴリズムを使って上手く処理したいです。
状態として「位置」と「駒の種類」の組み合わせを考え、それぞれが1頂点となるようなグラフを考えます。頂点の数は です。駒の移動も交換も、この頂点間の遷移として考えることができます。
頂点間の距離は、求める答えと同じように のペアで定義し、辞書順比較します。駒の移動と交換に対応する遷移を辺として追加し、ワーシャルフロイド法を用いると、ある状態からある状態までの最短距離を計算することができます。 なので のワーシャルフロイドが間に合うのがポイントです。
これを用いて答えを求めましょう。 を順に通る最短路を求めるにあたり、 「頂点 までの巡回が完了し、今の駒が であるときの、最初の状態からの最短距離」を としてDPができます。 を求めるときには、 すべてからの遷移を試して最も小さいものを採用します。これを まで回せば答えが求められます。
ACコード
(C++)Submission #44180256 - Codeforces
E. Side Transmutations
問題概要
種類の文字があり、その一部または全部で構成される 文字の文字列を考える。
整数 が与えられる。文字列 と は、 に以下の操作を0回以上行って と一致させることができる時、「等しい」と呼ぶ。
- を1つ選ぶ。操作対象の文字列の前 文字と後ろ 文字を、それぞれ反転して交換する。
「等しい」文字列は区別せず1種類と数えることにしたとき、 文字の文字列の種類数を数え上げて、 で割った余りを出力せよ。
制約
解法
問題文の操作を繰り返すことで、何が起こるでしょうか。例えば、2つの異なる箇所で操作を行うと、以下のように文字列が変化します。
の位置を区切りとして、左右それぞれ対応するペアを考えます。操作を繰り返すことで、任意のペアを入れ替えることができます。つまりこのペアをいくつか入れ替えることで一致するような文字列が、今回の問題でいう「等しい」文字列です。
ペアの入れ替えを独立に考えられるので、場合の数も独立に考えて掛け算することができます。 文字のペア、 文字のペア…というように、それぞれのペアについて独立に「反転で一致するものを同一視したときに文字列が何通りあるか」を数えて、これを掛け算すればよいです。( としておくと便利です)
ただし真ん中の部分の文字列は反転できないので、単純に「文字列が何通りあるか」を数えて掛け算します。それらを全て掛け合わせたものが答えです。
各ペアについての場合の数を考えます。 と置くと、求めたいものは「長さ の文字列を、反転すると一致するものは同一視して数えたときの場合の数」です。「反転すると一致する」文字列は、文字列が回文でない場合は1対1で相手が存在し、回文である場合は反転しても自分自身になるので相手が存在しません。
そのため反転を同一視しない全ての文字列の数を 、回文になっている文字列の数を とすると、回文になっていない文字列が同一視で半減することから、求めたい数は となります。具体的に計算すると 、 であるため、これらを全て計算していけばよいです。
ACコード
(C++)Submission #44153485 - Codeforces
F. Up and Down the Tree
問題概要
根を頂点1とする 頂点の根付き木と、整数 が与えられる。頂点1にトークンを置いた状態から、以下の操作を0回以上繰り返す。
頂点1にトークンを置いた状態からスタートし、なるべく多くの葉を訪れたい。その最大数を求めよ。
※接続されている辺の数が1本である頂点を葉と呼ぶが、今回は根である頂点1は葉ではないとして扱う。
解法
ある頂点から葉に降りたときに、元の頂点に戻って来れる場合と来れない場合があります。イメージとしては、葉が深いところにあり、途中で経由できるような葉もないときは戻ってこれません。
そのため、ある頂点以下の部分木でなるべく多くの葉を訪れようとすると、以下のような戦略になります。
- 全ての子に対して、まずは降りた後に自分自身に戻って来れる範囲で葉を回収する。
- 最後に1つだけ子を選んで降り、戻って来れない範囲の葉も回収し、操作を終える。
これは再帰的に処理していくことができます。つまりそれぞれの頂点に対して、
- = 頂点 以下の部分木で、 から始めて自分自身に戻って来ることが必要な場合、いくつ葉を回収できるか
- = 頂点 以下の部分木で、 から始めて自分自身に戻って来なくてもよい場合、いくつ葉を回収できるか
を葉のほうから決めていくような木DPができます。 が答えです。
このDPの遷移を考えます。ある頂点 に着目し、その子 を順に見ていきます。
- 子 から に( 以下のいずれかの葉を経由して)移動できる場合は、 から 側に降りて 個の葉を回収し、また に戻ってくることができます。もし最後に子 側に降りるのであれば、追加で 個の葉を回収することができます。
- 子 から に移動できない場合は、葉を回収して戻ってくることができません。ただし、最後に子 側に降りるのであれば 個の葉を回収することができます。
このようにそれぞれの子からの情報を得て、「戻ってこれる範囲で回収できる葉の数」の合計を とし、「最後に子 に降りるのであれば追加で回収できる葉の数」の最大値を に足したものを とします。
ここで「子 から親 に移動できるか」は、 から見て一番浅い位置にある葉の深さで決まります。そのような葉 から までの距離が 以下であれば、 という遷移ができます。
ACコード
(C++)Submission #44148563 - Codeforces
さいごに
Dが落ちたのは完全に不注意。実装がキツい問題だと思っていたけど、ちゃんと整理して書くと意外とスッキリ書けました。
Eは本番最後に方針が見えたので惜しかったです…。Fが解けそうだったので先にそっちに行きました。木DP好き。