HDU-3032--Nim or not Nim?(博弈+SG打表)
阿新 • • 發佈:2020-09-10
題目分析:
這是一個經典的Multi-SG遊戲的問題。
相較於普通的Nim遊戲,該遊戲僅僅是多了拆成兩堆這樣的一個狀態。即多了一個SG(x+y)的過程。
而根據SG定理,SG(x+y)這個遊戲的結果可以拆成SG(x)和 SG(y)遊戲的結果的xor。
因此,在我們求SG函式的過程中,我們只需要再列舉一下拆成兩堆的狀態,即可獲得Multi-SG遊戲每個狀態的SG函式。
但是題目中的資料範圍較大(達到了1e5的級別),因此我們考慮先打表找規律。打出SG函式後不難發現,存在規律:
順著上面的規律即可求出每一個狀態的SG函式,最後將這些SG函式結果全都xor起來即可。
#include<bits/stdc++.h> using namespace std; const int N = 1e6 + 100; typedef long long ll; ll n, a[N]; string Answer[2] = { "Alice\n","Bob\n" }; ll gsg(int x) { if (x % 4 == 0)return x - 1; else if (x % 4 == 3)return x + 1; else return x; } int main() { //freopen("in.txt", "r", stdin); ios_base::sync_with_stdio(false), cin.tie(0), cout.tie(0); int t; cin >> t; while (t--) { ll cnt = 0; cin >> n; for (int i = 1; i <= n; ++i) { cin >> a[i]; cnt ^= gsg(a[i]); } if (cnt == 0)cout << Answer[1]; else cout << Answer[0]; } }
SG打表程式碼:
//SG打表的程式碼 const int maxn = 105; int vis[maxn]; int sg[maxn]; void init() {//SG函式打表 for (int i = 0; i < 100; i++) {//列舉100個狀態 memset(vis, 0, sizeof(vis)); for (int j = 1; j <= i; j++) { vis[sg[i - j]] = true; } for (int j = 1; j < i; j++) {//列舉分成兩塊的狀態 vis[sg[j] ^ sg[i - j]] = true; } for (int j = 0;; j++) { if (!vis[j]) { sg[i] = j; break; } } } for (int i = 0; i < 100; i++) { cout << i << " " << sg[i] << endl; } }