あるエンジニアのAtCoder奮闘記

東京都港区にあるアミフィアブル株式会社のエンジニアが、AtCoderで解いた問題について振り返ったりしていく会社公認のブログです。

AtCoder ABC 129 E - Sum Equals Xor(Python)

irisruneです。最近AtCoder ProblemsのRecommendationsがスパルタに感じてきました。

問題

atcoder.jp

桁DPというヒントを見てしまいましたがほぼ自力です、多分。6/10の記事で実装が重いかと予想していましたが全然重くありませんでした。

import sys
import numpy as np
MOD = 1000000007


def main():
    bits = input()
    ans = 1
    pow3 = 1
    for i, b in enumerate(reversed(bits)):
        if b == '1':
            ans = ((ans * 2 % MOD) + pow3) % MOD
        pow3 = (pow3 * 3) % MOD
    print(ans)


main()

アルゴリズムの説明の前に今回使うXORの性質について、A+B=A xor BとなるのはAとB両方が1である(ビット)桁が1つもない場合となります。例えば、A=1,B=2の場合はA+B=A xor B=3と一致しますがA=1,B=3の場合はA+B=4,A xor B=2と一致しません。

逆に言えば今回使うXORの性質はこれ以外ないので、これさえ気付いてしまえばアルゴリズムを考えるのはそう難しくありません。具体的には、

  1. 0桁のL'については答えを1通り(A=B=0)とおく。
  2. L'に対し与えられたL一番右の桁から順番に追加する。
  3. 追加する桁の数字が0ならば、答えは変わらない。
  4. 追加する桁の数字が1ならば、答えを2倍し、3^\text{(追加する前の桁数)}を足す。

という方針で、ほとんど帰納法と言うべきアルゴリズムとなっています。

4.について補足をすると、1xxxxx_{(2)}\gt011111_{(2)}であることからL'=xxxxx_{(2)},11111_{(2)}それぞれについてA',B'が取り得る組み合わせ数を用いてL=1xxxxx_{(2)}についてA,Bが取り得る値の組み合わせ数を計算できます。

ここで、A,Bの一番左の桁がどちらか一方のみ1の場合は2通りあるので、L'=xxxxx_{(2)}についてA',B'が取り得る組み合わせ数を2倍した数の組み合わせがあります。

一方、A,Bの一番左の桁が両方0の場合は 11111_{(2)}についてA',B'が取り得る組み合わせ数を計算することになりますが、各桁について考えるとA'だけ1の場合、B'だけ1の場合、両方0の場合の3通りあるので3の累乗で求めることができます。

実装の軽さの割には細かくアルゴリズムを説明するのが大変難しいですね。解くだけならD問題より簡単だとは思いました。

雑記

  • 実はTLEを一度起こしていて、3の累乗を求めるのにpow関数を用いたのが原因でした。そもそも余りを求める必要がある問題でpow関数を用いたのもミスですが、計算量もかかるものなんですね。