[数学记录]AT3939 [ARC091D] Strange Nim
command_block
2021-02-13 23:25:57
**题意** : 有 $n$ 堆石子,每堆有 $a_i$ 个石子和一个常数 $k_i$,两人轮流操作,每次可以从任意一堆(假设为第 $i$ 堆)石子中取出至少一个至多 $\lfloor\frac{a_i}{k_i}\rfloor$ 个。
不能操作者输。问是否先手必胜。
$n\leq 200,\ a_i,k_i\leq 10^9$ ,时限$\texttt{2s}$。
------------
这是经典的 $\rm SG$ 和问题,求出每一堆的 $\rm SG$ 函数然后异或即可。
对于一堆石子 $(a,k)$ ,其后继状态为 $(a-\lfloor a/k\rfloor\sim a-1,k)$。
我们将 $\lfloor a/k\rfloor$ 相同的一组状态一起研究。
对于状态 $(ck\sim (c+1)k-1)$ ,一步最多拿走的石子数目均为 $c$。
显然状态 $(0\sim k-1,k)$ 都是无法操作的,故 $\rm SG$ 值为 $0$。
打表不难看出 $SG(ck,k)=c$。
而 $c$ 达到了后继状态总数,这说明 $SG(ck-1,k)\sim SG(ck-c,k)$ 为 $0\sim c-1$ 的排列。
考虑 $SG(ck+1,k)$ ,其后继状态为 $SG(ck,k)\sim SG(ck-c+1,k)$ ,其中 $SG(ck,k)$ 达到了后继状态数,无贡献。
其他部分 $SG(ck-1,k)\sim SG(ck-c+1,k)$ 相当于 $0\sim c-1$ 的排列去掉了 $SG(ck-c)$ ,故有 $SG(ck+1,k)=SG(ck-c)$。
进一步考虑 $SG(ck+2,k)$ ,能发现是 $0\sim c-1$ 的排列去掉了 $SG(ck-c+1)$ ,故有 $SG(ck+2,k)=SG(ck-c+1)$
依次类推,则有 $SG(ck+t)=SG(ck-c+t-1)$。
能写出如下递推公式。
$$
SG(a,k)=
\begin{cases}
0&(0\leq a<k)\\
a/k&(k|a)\\
SG(a-\lfloor a/k\rfloor-1,k) &(\rm otherwise)
\end{cases}
$$
进一步地, $SG((c+1)k)$ ,后继状态为 $SG(ck+c-1,k)\sim SG(ck,k)$ ,正是 $0\sim c$ 的排列,于是 $SG((c+1)k)=c+1$ ,能归纳证明结论。
若根据上述公式直接计算 $SG$ 函数,复杂度较劣。
考虑在转移中将 $\lfloor a/k\rfloor$ 相等的步骤一起做。
对 $(a,k)$ ,记 $x=\lfloor a/k\rfloor$ ,找出最大的 $y$ 使得 $y$ 次转移都是第三种情况。
即 $a-(y-1)(x+1)+1\geq xk$ ,则 $y=\left\lfloor\frac{a\bmod k-1}{x+1}\right\rfloor+1$。
这 $y$ 次转移都是第三种情况,所以可以直接令 $SG(a,k)=SG(a-(x+1)y,k)$。
考虑复杂度,当 $k\leq \sqrt{a}$ , $\lfloor a/k\rfloor$ 本身就 $\geq \sqrt{a}$ 。
当 $k> \sqrt{a}$ , $\lfloor a/k\rfloor$ 只有 $O(\sqrt{a})$ 种不同取值。
综上,总杂度为 $O(n\sqrt{a})$。
```cpp
#include<algorithm>
#include<cstdio>
using namespace std;
int f(int n,int k)
{
if (n<k)return 0;
if (n%k==0)return n/k;
int x=n/k,y=(n%k-1)/(x+1)+1;
return f(n-y*(x+1),k);
}
int n;
int main()
{
scanf("%d",&n);
int ans=0;
for (int i=1,a,k;i<=n;i++){
scanf("%d%d",&a,&k);
ans^=f(a,k);
}puts(ans==0 ? "Aoki" : "Takahashi");
return 0;
}
```