[AtCoder]ABC027 倍々ゲーム

投稿日: 更新日:

問題:https://atcoder.jp/contests/abc027/tasks/abc027_c

解法

x=Nx = Nのところから考えていきます。

例として、N=10N = 10の場合を考えます。

もし、xxが10~6の時、2x,2x+12x, 2x+1のどちらを選んでも10を超えるので負けが確定します。

次に、xxが5~3の時、2x,2x+12x, 2x+1どちらを選んでも、次は10~6と前述の負けが確定する範囲の数を相手に渡すことができます。つまり、5~3は勝ちが確定している範囲です。

次に、x=2x=2の時、2x,2x+12x, 2x+1どちらを選んでも勝ちが確定している5~3の数字を相手に渡すこととなります。つまり、2は負けが確定します。

最後にx=1x=1の時2x2xを選ぶことによって負けが確定している2を相手に渡すことができます。つまり、x=1x=1は勝ち確定です。よって、高橋君が勝者となります。

このようにNNから勝ち負けの範囲を求めていくと上手く求まります。

一般化する

勝ち範囲から負け範囲を求める。

勝ち範囲の下限がaaとします。この時、隣の負け範囲の下限は2x,2x+12x, 2x+1どちらを選んでも、aa以上になってしまう数です。すなわち、小さいほうの手である2x2xを選んだとしてもaa以上になる数です。そのような数は、a/2\lceil a/2\rceil(切り上げ)です。

負け範囲から勝ち範囲を求める。

負け範囲の下限をbbとします。この時、隣の勝ち範囲はの下限は2x,2x+12x, 2x+1どちらかを使って、bb以上にできる数です。すなわち、大きい方の手である2x+12x+1を使った時、bb以上にできる数です。不等式を変形することで求まります。小数点以下は切り上げです。

2x+1bxb12\begin{align} 2x + 1 \geqq b \\ x \geqq \left\lceil\frac{b-1}{2}\right\rceil \end{align}

実装

解法にあるように、どちらの範囲を求めるにしても1つ前の下限が必要です。ここで、N+1N+1の時、相手が既に負けている状態なので最初の勝ち範囲の下限とできます。

割り算の切り上げはa/ba/bの時、以下の式で示されます。

ab=a+(b1)b\left\lceil\frac{a}{b} \right\rceil = \frac{a + (b-1)}{b}

今の範囲が勝ちかどうかをwinで管理しています。

#include <bits/stdc++.h>
using namespace std;

int main(){
  long long n;
  cin >> n;
  n += 1;
  bool win = true;
  while(n > 1){
    if(win){
      n = (n+1)/2;
    }else{
      n = n/2;
    }
    win = !win;
  }

  if(win){
    cout << "Takahashi" << endl;
  }else{
    cout << "Aoki" << endl;
  }

  return 0;
}

書いた人

profile_image

お茶の葉

物理とプログラミングが好きな人