\(\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;
}