题目描述
题解
这是一道博弈论SGSGSG函数的题。
我们记SG(i)SG(i)SG(i)表示大小为i的石头的SGSGSG函数。边界条件是:SG(0)=0SG(0)=0SG(0)=0.
为了得到每一份SGSGSG值,我们需要对每一个扩展的状态进行mexmexmex运算。
对于每一个扩展的状态,若由若干个毫不相关的状态组成,则对应得mex运算值需要进行异或运算来解决。例如本提中:对于一个状态iii如果分成了状态j1,j2,j3,j_1,j_2,j_3,j1,j2,j3,那么选取第二堆石头的的运算值是SG(j1) xor SG(j3).SG(j_1)\ xor\ SG(j_3).SG(j1) xor SG(j3).
具体的求解运算值的方法,我们可以先求解出扩展状态的每一个SG值得xor和,再分别异或每一个数即可;同样以上面的那个例子为例,可以先求出ans = SG(j1) xor SG(j2) xor SG(j3)ans\ =\ SG(j_1)\ xor\ SG(j_2)\ xor\ SG(j_3)ans = SG(j1) xor SG(j2) xor SG(j3),如果要求解取第二堆石头的值,我们只需要ans xor SG(j2)ans\ xor\ SG(j_2)ans xor SG(j2)即可。这样我们就得到了求解每一份SG值得方法。
我们如果要判断是否有必胜策略,我们只需要对所有初始石头的SGSGSG值进行一遍异或运算;如果异或和大于000说明必胜,否则必败。
代码如下:
#include <bits/stdc++.h>
using namespace std;
int a[110];
int f[1010];
int SG(int x)
{
int ans = 0, vis[1010] = {};
if (f[x] ^ -1) return f[x];
if (x == 1) return f[x] = 0;
for (int i=1;i*i<=x;++i)
if (x%i == 0)
{
ans ^= SG(i);
if (i*i ^ x && i > 1) ans ^= SG(x/i);
}
for (int i=1;i*i<=x;++i)
if (x%i == 0)
{
vis[ans^SG(i)] = 1;
if (i*i ^ x && i > 1) vis[ans^SG(x/i)] = 1;
}
for (int i=0;;++i)
if (vis[i] == 0)
return f[x] = i;
}
void work(int n)
{
for (int i=1;i<=n;++i) scanf("%d",a+i);
int ans = 0;
for (int i=1;i<=n;++i) ans ^= SG(a[i]);
if (ans != 0) puts("freda");
else puts("rainbow");
return;
}
int main(void)
{
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
int n;
memset(f,-1,sizeof(f));
while (scanf("%d",&n) != EOF)
work(n);
return 0;
}