
题目大意
给你n个石子堆,然后Alice和Bob开始博弈。每个人每次可以从第i堆里面选出[ 1 , ⌊ a i k i ⌋ ] [1,\lfloor\frac {ai}{ki}\rfloor][1,⌊kiai⌋]个石子。然后最后不能选的人就GG。
思考历程/题解
其实还是挺简单的,sg函数裸题。
先是想了个很暴力的dp方程,然后自以为可以线段树优化,结果发现很GG。
然后就想到了sg函数一般解法打表——分类讨论。
然后就一层一层地列,于是就发现了一些奇妙的规律。
当1 < = x i < k i 1<=xi<ki1<=xi<ki时:0 , 0 , 0 , … , 0 0,0,0,…,00,0,0,…,0
当k i < = x i < 2 k i ki<=xi<2kiki<=xi<2ki时:1 , 0 , 1 , 0 , … 1,0,1,0,…1,0,1,0,…
当2 k i < = x i < 3 k i 2ki<=xi<3ki2ki<=xi<3ki时:2 , … 2,…2,…
总之,能够观察到一个奇妙的结论:s g [ x ] = x k sg[x]=\frac x ksg[x]=kx当且仅当x % k = 0 x\%k=0x%k=0
然后我们就再通过sg函数mex的定义得到:s g [ x ] = s g [ x − ⌊ x k ⌋ − 1 ] sg[x]=sg[x-\lfloor\frac {x}{k}\rfloor-1]sg[x]=sg[x−⌊kx⌋−1]
于是就可以开心地递归啦~
WAIT A MINUTE!
当k很大的时候我们每次往前跳就会跳很短的距离。
这样时间复杂度还是会卡到O ( a i ) O(ai)O(ai)
于是考虑平衡规划一下。
- k<=100000,直接递归
- k>100000,考虑优化往前跳的过程。
- 由于k比较大,所以一层的数也比较大。
- 那就直接优化一个点在一层往前跳最远能跳到哪里。
- 求出来之后如果不是最前面的点,则继续跳到下一层递归。
- 否则输出x k \frac x kkx
然后就没了。
时间复杂度大致是O ( n a i ) O(n\sqrt {ai})O(nai)
代码
#include <iostream>
#include <cmath>
#include <cstring>
#include <cstdio>
using namespace std;
int n;
long long x[210],k[210],a[210];
long long findsg(int x,int k)
{
if (x<k) return 0;
if (x%k==0) return x/k;
if (k>=100000)
{
long long op=x%k;
long long oq=op%(x/k+1);
x=k*(x/k)+oq;
if (oq==0) return x/k;
return findsg(x-x/k-1,k);
}
else
return findsg(x-x/k-1,k);
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%lld%lld",&x[i],&k[i]);
a[i]=findsg(x[i],k[i]);
}
long long ans=0;
for (int i=1;i<=n;i++)
{
ans=ans^a[i];
}
if (ans!=0) printf("Takahashi\n");
else printf("Aoki\n");
}