ARC103 参加記録&解説(C~E)
AtCoder Regular Contest 103 - AtCoder
結果はCE2完+D部分点で73位。黄色復帰!
今回、この獲得点数の人が180人くらいいたので、スピードも功を奏したと思います。
C~Eを振り返ります。
C - /\/\/\/
↑半角バックスラッシュが円マークになるので全角にしました…
解法
偶数番目と奇数番目に分けて考えます。変更個数を少なくするためには、それぞれ元から一番多かった数字を残したくなります。
一番多かった数字が異なっていれば、それらをそのまま採用できます。もし同じ数字だった場合は 数列に現れる数はちょうど2種類 という条件に反しないよう、片方を諦めて2番手にする必要があります。「奇数1番手+偶数2番手」と「奇数2番手+偶数1番手」の両方を試して、登場回数合計の多い(=変更が少ない)ほうを採用します。
ACコード
- (Ruby)Submission #3291693 - AtCoder Regular Contest 103
- (C++)Submission #3305974 - AtCoder Regular Contest 103
D - Robot Arms
解法
必要条件を導出する
まず、解が存在する必要条件を考えます。いったん と の区別をなくし、「それぞれの腕が正負どちらの方向に伸びているか?」を考えます。これを符号付きで全部足し算したものが、最終到達地点の座標を足したもの、つまり となります。
ある腕を、負から正に変更したとします。その腕の長さを とすると、 だったものが になるため、最終地点の座標の和は 加算されます。逆に正から負に変更した場合は です。
より一般的に言うと、腕の正負を入れ替えた時、その長さの2倍だけ座標和が変動します。奇数の長さで変動することはありません。つまり作らなければいけない最終到達地点 について座標和を考えると、それらの差は全て偶数である、言い換えると偶奇が一致している必要があります。
まずこれが必要条件になります。
構成方法を考える
では、構成方法を考えましょう。作りたいパターン数が というのはそこまで多い数ではないですが、何と使える腕の本数が しかありません。1つ1つのパターンを見て腕の長さを決めていく方法は無理そうです。どんな数でも作れる「必勝法」を考えたいです。
「どんな数でも作れる」の定石は 進数です。2進数を使うとすると、作りたい座標は であり、腕40本でも足りそうな雰囲気です。ただ、 をそれぞれ作るとなると、それぞれに腕が30本必要そうに見えますし、不必要な腕をどう調整するかも難しく思えます。
ではどうすればいいかというと、 を それぞれ作る をやめて、同時に作る ようにします。1本の腕の方向を選ぶことで 両方の値を操作できれば、腕の数が足りそうです。もちろんこのままでは無理ですが、それを可能にするのが座標変換です。
として、 座標系を考えます。これは、座標平面を45度回転しているような変換です。
このように変換すると、腕の4方向が「 と それぞれ、正負どちらの方向にはたらくか」の4通りに対応します。2つの値を同時に操作することができて、かつ「 は増やしたいけど は減らしたい」みたいな要求4パターン全てに対応できるのが嬉しいポイントです。
目的地の座標も変換して と書くことにします。そうすると と値の範囲が変わりますが、腕は40本あるので大丈夫です。各腕に を割り振って、これらの足し算引き算だけで目的の値を作ることを考えます。
普通の0 or 1の2進数とは違い、-1 or 1の2進数(?)となっていますが、以下のように「大きい方から選ぶ」ことを考えればよいです。
常に目的の値に近づく方向に正負を採用していけば、最後は目的の値に辿り着きます。実際には目的の値が「3」のような小さな数のときでも、もっと大きな値を組み合わせて作ることになりますが、その場合でも考え方は同じです。
ただしこれがキレイに収まるのは が奇数の場合で、偶数の場合は最後にもう1つ1を付けて調整してあげる必要があります。
※ と の偶奇は一致するため、片方では追加で1が必要だけどもう片方では余計、という事態にはならないので安心です。
これで解けました。解法をまとめると、
- まず目的地の座標和の偶奇が一致しているかチェック。一致していなければ解なしで終了。
- 座標変換を行う。
- 腕の長さとして の値を用意する。目的地の座標和が偶数の場合、さらに1を足す。
- それぞれの目的地ごとに、大きい方の腕から方向を決めていくことで目的の値を作る。
これで解けます。
ACコード
※部分点解法は、全部の腕の長さを1にしても足りるので、偶奇チェックした後は全部1で作ります。部分点獲得コードはこちら。
E - Tr/ee
解法
必要条件を導出する
これも、まず必要条件を考えます。
- サイズ1の連結成分は必ず作れる。葉だけを切断するとサイズ1の連結成分になるため。
- サイズ の連結成分は必ず作れない。サイズ の木を切断するとそれぞれの部分木のサイズは 未満になるため。
- サイズ の連結成分が作れるならば、サイズ の連結成分も作れる。切断によってサイズ の連結成分ができたとき、もう片方はサイズ になっているため。
入力がこれらの条件に反する場合、条件を満たす木は構築不可能です。
構成方法を考える
サイズの小さいほうから考えていきましょう。根付き木として考えたとき、深いほうから順に構成していきます。
先ほど見たように、サイズ1は必ず作れます。では「サイズ2が作れる」と「サイズ2が作れない」は、どのように差を付ければ良いでしょうか。
サイズ2を作りたい場合、サイズ2の部分木の上に親を作ればよいです。逆に作りたくない場合は、サイズ2の部分木で親となっている頂点に、さらに子を生やせばよいです。これを繰り返すことで目的の木を作ることができて、もう少し丁寧に書くと
- 構築している木がサイズ で、次に 番目の頂点を付ける時に、
- サイズ の連結成分を作りたい場合は、現在親となっている頂点のさらに親に 番目の頂点を置く。
- サイズ の連結成分を作りたくないときは、現在親となっている頂点の子として 番目の頂点を置く。
このように構成することができます。以下は「1, 3, 6, ...」が作れる木の例です。
ACコード
さいごに
Dの満点解法、2進数よりも45度回転の発想のほうが難しいかなと個人的には思いました。さっさと部分点だけ獲得したのは、結果的には正解でした。
木の問題はけっこう好きなので、Eは速く解けたかなと思います。Fも好きなタイプの問題だったので、本番で通せるようになりたい…