【AT3939】[ARC091D] Strange Nim(博弈论)

点此看题面

  • (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;
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/AT3939.html