【題解】Acwing 235. 魔法珠
阿新 • • 發佈:2021-10-08
\(\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; }