洛谷 P1247 取火柴遊戲
阿新 • • 發佈:2020-09-10
扯
不學博弈了,怕了怕了
思路
很顯然的一個nim遊戲板子。
直接求出每一堆火柴數量的異或和,如果異或和為 \(0\) 則先手必敗,否則先手必勝。
nim遊戲的結束條件很簡單,當所有火柴的數量都為 \(0\) 時,下一個拿火柴的人就失敗了,此時的局面是 \(0\bigoplus0\bigoplus0...\bigoplus0=0\)。
設每一堆的石子數為 \(a_i,i\in{[1,n]}\)。
- 對先手來說,如果當前局面是 \(a_1\bigoplus a_2\bigoplus a_3...\bigoplus a_n=k\)。那麼當前石子中必定存才一個 \(a_i\) 滿足 \(a_i\) 的二進位制表示裡存在 \(k\)
- 對於先手來說,如果當前局面是 \(a_1\bigoplus a_2\bigoplus a_3...\bigoplus a_n=0\),那麼我們不可能將某個 \(a_i\) 異或一個數字之後使得 \(a_1\bigoplus a_2\bigoplus a_3...\bigoplus a_n=0\),此時先手必敗。
輸出方案時從小到大列舉每一個 \(a_i\),如果 \(a_i\bigoplus k<a_i\)
程式碼
/* Author: Loceaner 知識點: nim遊戲 */ #include <cmath> #include <cstdio> #include <vector> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int A = 5e5 + 11; inline int read() { char c = getchar(); int x = 0, f = 1; for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1; for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48); return x * f; } int n, a[A], xo; int main() { n = read(); for (int i = 1; i <= n; i++) { a[i] = read(), xo ^= a[i]; } if (!xo) return cout << "lose\n", 0; for (int i = 1; i <= n; i++) { if ((a[i] ^ xo) < a[i]) { cout << a[i] - (a[i] ^ xo) << " " << i << '\n'; for (int j = 1; j <= n; j++) { if (i == j) cout << (a[i] ^ xo) << " "; else cout << a[j] << " "; } return 0; } } return 0; }