TopCoder SRM 493 Div 1 Easy StonesGame

問題概要

N個の石が一列に並んでいて、M個目だけが白で他は黒。
二人のプレイヤーが、手番に白石が入っている連続するK個の石を逆順にするゲームをする。
最初にL番目に白石を置いたプレイヤーが勝ちである。


お互いが最善の戦略を取ったときどちらが勝つか求めよ。
N≦100万とする。

解法

3手以上ゲームが長引くなら勝者はない。
なぜなら、相手と同じ区間を選べば、相手の番にまた同じ盤面が来て無限ループさせることができるため。


1手目でLに白石を移動させらるなら先手の勝ち。
そうでないとき、反転の仕方を全て試して、どれも先手が負けるなら後手勝ち、そうでないなら引き分けである。


1手でLに白石を移動させられるかはO(1)で判定できる。
まず、LとMの距離がK未満であること、かつM-L+1とKの偶奇が一致することが必要条件。
それから、L,Mを対称になるよう長さKの区間に入れたときに、両端が1以上N以下に収まれば必要十分である。

●●○●●▲●●
 └────┘←大きさK
○を▲に移動させたいとき、こういう区間が取れれば良い。

上の説明で言っているのは、Kの中で○と▲が対称にできるか(偶奇が一致しないとダメ)、
また、対称にしたときにKが両端の石からはみ出ないかということ。

ソースコード

bool can(int N, int M, int K, int L){
	if(M>L){
		M=N-M+1;
		L=N-L+1;
	}
	if(L-M>=K||(L-M+1)%2!=K%2)return 0;
	if(K%2){
		return 1<=(M+L)/2-K/2&&(M+L)/2+K/2<=N;
	}
	return 1<=(M+L+1)/2-K/2&&(M+L)/2+K/2<=N;
}
class StonesGame {
	public:
	string winner(int N, int M, int K, int L) 
	{
		if(can(N,M,K,L))return "Romeo";
		bool notlose=0;
		for(int i=1;i<=N;i++){
			if(i+K-1<M)continue;
			if(i>M||i+K-1>N)break;
			int m=2*i+K-M-1;
			if(!can(N,m,K,L))notlose=1;
		}
		return notlose?"Draw":"Strangelet";
	}
};