ARC091 F : Strange Nim(900)

作成日:2018.03.14
最終更新日:2018.03.14

問題概要

問題本文

 山が$N$個あり、$i$番目の山には$A_i$個の石がある。 2人が交互に操作「$i$個目の山を選び、$1$個以上$X/K_i$個以下の個数の石をその山から取り除く」を行う。 先に操作を行えなくなった方が負けである時、先手と後手のいずれに必勝手順があるかを判定せよ。
制約
$1 \leq N \leq 200$
$1 \leq A_i , K_i \leq 10^9$

考え方

 二人で交互に同じ操作を行うゲームなので、Grundy数を考える。 各山は独立しているから、山ごとにGrundy数を求めて、それらのxorをとれば良い。 すなわち、1つの山のGrundy数が求められれば良い。
 石が$a$個ある時のGrundy数を$g_a$とする。 例えば$K=3$とすると、$g_0,g_1,...$は$0$,$0$,$0$,$1$,$0$,$1$,$2$,$0$,$1$,$3$,$2$,$0$,$4,...$となる。 $[a/K] = m$の時、$g_a$は$g_{a-1},...,g_{a-m}$に含まれない最大の非負整数である。 このことから$g_a \leq m$であり、周期が$m+1$であることが分かる。 さらに$m$の値が増える時、すなわち$a$が$K$の倍数の時、$g_a = a/K$となることも分かる。
 以上のことを用いると$g_a$が$K$の倍数か$K$未満の時は$O(1)$で求まるが、そうでない時は$g_a = g_{a-[a/K]-1}$を繰り返し適用する必要がある。 この計算量は$[a/K]$の値ごとに行う引き算の回数を考えると$K \sum_{i \leq [A/K]}(1/i)$であるから、$O(A \: log(A/K))$であることが分かる。 これでは間に合わないので工夫する。 計算量を求めた時に$[a/K]$の値ごとにまとめたが、引き算もその値ごとにまとめることができる。 すなわち$t([a/K]+1)$が$a\%K$以上になる最小の$t$だけまとめられる。 この$t$は$(a\%K)/([a/K]+1)$である。 引き算は高々$A/K$回であり、$A/K \geq K$の時には$A-A/K$$=A(K-1)/K$であるから指数関数的に$A$が減少していく。 よって計算量は$O(min(A/K,$$K \: log \: A))$$\leq O(\sqrt K \: log \: A)$であるから十分高速である。

ソースコード

マクロ等はこちら

int N;

int solve(int a , int k){
	if(a<k) return 0;
	if(a%k==0) return a/k;
	int d = a/k + 1;
	d = (a%k+d-1)/d*d;
	return solve(a-d,k);
}

int main(){
	cin >> N;
	int g = 0;
	repp(i,0,N){
		int A,K;
		cin >> A >> K;
		g ^= solve(A,K);
	}
	cout << (g==0?"Aoki":"Takahashi") << endl;
	return 0;
}

解法まとめ

 $A,K$の組ごとにGrundy数を求め、それらのxorが$0$なら後手、そうでないなら先手が勝つ。(13-19行)
 $A,K$が与えられた時のGrundy数は、$A \lt K$の時$0$、$A$が$K$の倍数の時$A/K$、いずれでもない時は$A$を$A-A/K-1$に置き換えたものに等しい。 ここで$A/K$が変わらない部分はまとめて計算する。 具体的には$\lceil (A\%K)/([A/K]+1) \rceil$回分をまとめることができる。(3-9行)

ARC091の他の問題 C D E

自己紹介

プログラミングとか合成音声とか
詳しくはこちら
twitter

プライバシーポリシー

個人情報利用についてはこちら

最終更新日:2023.03.05

お問い合わせ

このページに関するお問い合わせはこちら

競技プログラミングMENU

問題を解くまでの道のり

その他

全体MENU

寄付モドキ

0冂から10000冂まで
詳しくはこちら