ABC172 Unfair Nim
これも以前写経して通したけどわからなかったやつ。今度こそ理解するぞ。
問題概要
Nimをする。一つ目の山から二つ目の山に0個以上の石を移し替えることで、後手必勝にできるか判定せよ。
また、必勝にできる場合は移し替える最小の石の個数を求めよ
という問題。
解法
Nimの説明は省略します。Nimで後手必勝 = 全部の石の数のXORが0 という有名事実があるので、「一つ目の山から二つ目の山に0個以上の石を移し替えることで、全ての数のXORを0にできるか判定せよ」という問題になります。
以下、各山の石の数をと置きます。一つ目の山は, 二つ目の山はです。
と以外の値は不変なので、三番目の山からN番目の山までの排他的論理和も不変であることは明らかです。この不変な排他的論理和をと置きます。
すると全ての数のXORを0にするには、とのXORがにする必要があります。
よって、となるような最小のを求める問題に言い換えることが出来ます。
まあここまではNimさえ知っていればすんなり来れますね。問題はここからです。
をナイーブに全探索して試していけば当然解くことが出来ますが、制約がなので、まあ2sec以内に解くことは不可能です。
仕方がないので別の方法を考えます。まず、XORなので2進法に変換して桁ごとに見ていくのが典型です。正直こんなの貪欲か桁DPのどっちかしかないんだよねのお気持ちになります。
僕の実力では貪欲の正当性を自力で示すことは難しいので、まずは桁DPを考えます。そのうち貪欲を理解したらそれも書くかも。
色々試行錯誤した結果、繰り上がりが発生して面倒なことがわかるので、下の桁から値を決めていく方針で行きます。
として、, とします。
すると求めたい値は
という条件を満たす最大のとなります。
の下からi桁目までとの下からi桁目までが一致するようなの最大値
と定義します。最初をそのまま決めようと思ったら繰り下がりなんかも出てきて嫌になったのでやめました。
自力だとここから先に行けずに躓いてしまったのですが、原因は以下です。
- との二つについて決めていく考え方を持てなかった
- 下から決めていくとき、未満フラグ(以下フラグ?)をどう管理して遷移していけばいいのかわからなかった
2はほぼ知識で何とか出来るところですし、2さえ分かっていればの試行錯誤もスムーズにいくはずなので、覚えちゃいたいですね。
というわけですぬけさんの解説動画を見ました。
とをそれぞれ桁目まで決めていて、繰り上がりの有無、以下か否かをそれぞれとのフラグで持ったときのの最大値
を考えます。
まず桁目のとの値は]の四つ考えられるので、四通り全部試します。
そのうえで、その桁まで見たときのとの排他的論理和がと等しいか、和がと等しいかをチェックして、両者とも等しいときのみ遷移します。
これはややこしく思えますが、排他的論理和は各桁ごとに完全に独立、和も繰り上がりしているかどうかさえ事前に分かっていれば各桁ごとに独立です。
だから繰り上がりの有無を状態として持っている必要があるんですね。
さて、そこまでの値が以下か否かを持つフラグの遷移は少し考える必要があります。
各桁ごとに見たときに、その桁の値がのそれよりも「大きいか/小さいか/同じか」の三通りで場合分けを考えます。
大きい時
それまでがどんな状態だろうが、「以下ではない」フラグの方に遷移します
小さい時
それまでがどんな状態だろうが、「以下」フラグの方に遷移します
同じとき
それまでの状態と同じフラグの方に遷移します
これで遷移が全部わかったので、実装するとACとなります。ちなみにbit演算でバグり散らかしました。今度から (X >> i) & 1ll
って書きます.......。
うーん、見るべき条件が多いので、それらをしっかりと満たしながら遷移できるDPを考えるのに参ってしまいました。要訓練ですね。
maspyさんバージョンのDP
maspyさんの解法を見たらすごくて笑っちゃった。桁DPは総じて添え字地獄になりがちとか思ってたんですが、これなら実装が簡単ですね。
詳細は↑です。
DPできる問題は再帰的な構造をしているという事実が頭から抜けていました。こんなにシンプルに考えられるの、魔法みたいですね。
何というか、僕は頭が弱いから、DPを難しく考えてしまうのだなと思いました。