單堆遊戲獨立,整體 SG = 所有堆 SG 異或和。
sg[x] = mex{ sg[y] ^ sg[x-y] | 1 ≤ y < x/2 }(利用 x≠y 對稱性,只枚舉 y < x/2)。
基礎情況:sg[1]=sg[2]=0(無法分割)。
const int MAXA = 10000;
int sg[MAXA + 1];
bool vis[MAXA + 1];
void calcSG() {
sg[1] = sg[2] = 0;
for (int x = 3; x <= MAXA; ++x) {
fill(vis, vis + MAXA + 1, false);
for (int y = 1; y < (x+1)/2; ++y) // y < x-y 保證不等
vis[sg[y] ^ sg[x-y]] = true;
for (int g = 0; ; ++g)
if (!vis[g]) { sg[x] = g; break; }
}
}
優化觀察:sg[x] 增長極慢(實測最大值 < 20),vis 數組可縮小至 32 或 64,大幅加速。
int main() {
calcSG();
int T; scanf("%d", &T);
for (int cs = 1; cs <= T; ++cs) {
int n; scanf("%d", &n);
int xorsum = 0;
for (int i = 0, a; i < n; ++i) {
scanf("%d", &a);
xorsum ^= sg[a];
}
printf("Case %d: %s\n", cs, xorsum ? "Alice" : "Bob");
}
return 0;
}
打印前 100 個 SG 值可發現:
sg[1]=0, sg[2]=0, sg[3]=1, sg[4]=1, sg[5]=2, sg[6]=2, sg[7]=3, sg[8]=3...
規律:sg[2k] = sg[2k+1] = k-1(k≥1)。
證明可用數學歸納法,掌握後可 O(1) 計算 SG,無需預計算表。
vis 縮小極快(< 0.1s)。// 通用 SG 模板
int sg[MAX_STATE];
bool vis[MAX_SG];
void getSG() {
sg[terminal] = 0;
for (state = terminal+1; state <= MAX_STATE; ++state) {
clear(vis);
for (next : moves(state)) vis[sg[next]] = true;
sg[state] = mex(vis);
}
}