【題解】Acwing 235. 魔法珠

Acwing 235. 魔法珠

\(\text{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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章