T1
题面

解题
- 判断先手与后手输赢的条件为 \(SG(a_i)\) 的异或和是否为 \(0\),问题转化为如何求 \(SG(x)\)。
- 假设某堆石子的石子数量为 \(m=n^2+k,k\in \{0,1,\dots,2n\}\)。当 \(k\ne0\) 时,其后继状态的石子数均为 \(n^2-ln,l\in\{0,1,\dots,n\}\);当 \(k=0\) 时,其后继状态的石子数为 \(n^2-ln,l\in\{1,2,\dots,n\}\)。故只需要求得 \(SG(n^2),SG(n^2+1),n\in\{0,1,\dots,[\sqrt{\max\{a\}}]\}\)。
- 因为每个待求的 \(SG(S)\) 的后继状态只有 \([\sqrt{S}]\) 个,故可通过记忆化搜索使得时间复杂度为 \(\mathcal O(max\{a\})\)。
- 易错点:用 vector 取计算 mex 时,需要对其中元素进行去重(否则导致 \(SG\) 计算错误)。
代码
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
int sg[maxn];
int f(int x)
{
int y=(int)sqrt(x+0.5);
int ex=y*y+(y*y==x?0:1);
if(sg[ex]!=-1) return sg[x]=sg[ex];
set<int> mex;
int op=y*y-(y*y==ex?y:0);
for(int i=op;i>=0;i-=y)
mex.insert(f(i));
set<int>::iterator it; int k=0;
for(it=mex.begin();it!=mex.end();it++,k++)
if(k!=*it) {sg[ex]=k;break;}
if(sg[ex]==-1) sg[ex]=mex.size();
return sg[x]=sg[ex];
}
signed main()
{
memset(sg,-1,sizeof(sg));
sg[0]=0;
int n,ans=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int a; scanf("%d",&a);
ans^=f(a);
}
if(ans==0) printf("Second\n");
else printf("First\n");
return 0;
}