【题解】Acwing 235. 魔法珠

Acwing 235. 魔法珠

( ext{Solution:})

其实思考下发现不好做之后就知道必须要 SG 函数了……拿简单题复习一下 SG 函数吧 顺便也碰到了一些细节

首先我们发现,每一个数字都会被拆分成若干其他数字,并且会被拿走。而且也容易证明这是一个公平组合游戏。那么 SG 函数在这一题就成立了。我们来分析如何应用:

首先,每个数字都是一个小游戏,所以它们必然有自己的 SG 函数值。所以第一个任务应该是求出每个数字的 SG 函数。

其次,整体局面就是一个若干 SG 函数的拼接,所以我们求出 SG 函数之后利用 SG 函数引理异或起来即可。

那么如何求一个数的 SG 函数?这里有细节要注意。

首先,通过定义我们容易知道,一个数的 SG 函数值应当是其所有后继状态的 SG 函数的 mex 运算结果。而这句话乍看很简单,但也是有细节要注意的:

我们求的是其所有后缀状态的 SG ,而非后缀产生的游戏的 SG !

这一题中体现尤为明显。因为在暴力搜索求 SG 的时候,我们将其质因数分解之后,很容易直接求其所有质因数 SG 的 mex 。这显然是错的,因为这些质因数对应的游戏并不是这个游戏操作后的状态。

对应的状态应当是,由当前执子者拿掉一堆石子后的状态。所以我们要求的 SG 应当是选出 (n-1) 个数的 SG 的异或值。

分析到这里细节就差不多了。直接暴搜即可。

#include<bits/stdc++.h>
using namespace std;
const int N=1001;
int sg[N],a[N],n;
int SG(int x){
	if(~sg[x])return sg[x];
	vector<int>vec;
	vec.emplace_back(1);
	int v=x;
	for(int i=2;i*i<=x;++i){
		if(v%i==0){
			vec.emplace_back(i);
			if(i*i!=x)vec.emplace_back(x/i);
		}
	}
	sort(vec.begin(),vec.end());
	unordered_map<int,int>vis;
	int sum=0;
	for(int i=0;i<(int)vec.size();++i)sum^=SG(vec[i]);
	for(int i=0;i<(int)vec.size();++i)vis[sum^SG(vec[i])]=1;
	sg[x]=0;
	while(vis[sg[x]])++sg[x];
	return sg[x];
}
int main(){
	freopen("in.txt","r",stdin);
	for(int i=0;i<=1000;++i)sg[i]=-1;
	sg[1]=0;sg[0]=0;
	while(~scanf("%d",&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("rainbow");
		else puts("freda");
	}
	return 0;
}
原文地址:https://www.cnblogs.com/h-lka/p/15379558.html