- 有(n)堆石子,每堆石子初始有(a_i)个,且附带一个参数(k_i)。
- 每次可以从一堆石子(假设是第(i)堆,当前剩(x_i)个石子)中取出(1simlfloorfrac{x_i}{k_i} floor)个石子,判断谁必胜。
- (nle200,a_i,k_ile10^9)
(SG)函数
根据(Nim)游戏的基本知识我们知道可以分别求出每堆石子的(SG)函数,然后异或在一起就是全局的(SG)函数了。
一个显然的转移式:
[SG(x)= exttt{mex}_{i=1}^{lfloorfrac xk
floor}SG(x-i)
]
看起来很诡异,但我们仔细想想便会找到一个性质,就是(x)的前(lfloorfrac xk floor+1)位应该是一个(0sim lfloorfrac xk floor)的排列!
但如果(x)是(k)的倍数时会存在例外,此时它的前(lfloorfrac xk floor)位是一个(0sim lfloorfrac xk floor-1)的排列,因此(SG(x)=lfloorfrac xk floor)。
证明只需归纳即可,应该还是比较容易的。
那么我们可以列出一个简单的递推式:
[SG(x)=egin{cases}0&x<k,\lfloorfrac xk
floor&k|x,\SG(x-lfloorfrac xk
floor-1)&x>k&&k
ot|xend{cases}
]
主要是考虑第三种转移,直接搞复杂度是(O(k))的,如果(k)很大可能会挂。
但我们发现对于(lfloorfrac xk floor)相等的连续一段其实可以放一起转移,那么就只会转移(O(lfloorfrac ak floor))次。
二者一结合,复杂度就是(O(sqrt a))的,正确了。
代码:(O(nsqrt a))
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
using namespace std;
int n;I int SG(CI x,CI k)//求SG函数
{
if(x<k) return 0;if(!(x%k)) return x/k;//x<k和x是k倍数的两种情况
RI y=x/k*k,d=x/k+1,t=(x-y)/d;return SG(x-max(t,1)*d,k);//x/k相同的一段一起转移
}
int main()
{
RI i,x,k,t=0;for(scanf("%d",&n),i=1;i<=n;++i) scanf("%d%d",&x,&k),t^=SG(x,k);//把每堆石子的SG函数异或起来
return puts(t?"Takahashi":"Aoki"),0;
}