1. 程式人生 > 實用技巧 >洛谷 P1247 取火柴遊戲

洛谷 P1247 取火柴遊戲

不學博弈了,怕了怕了

思路

很顯然的一個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\)
    的最高位,否則 \(k\) 的最高位異或和應該是 \(0\)。令這個 \(a_i\bigoplus k\),局面就變成了 \(a_1\bigoplus a_2\bigoplus a_3...\bigoplus a_n=0\),此時先手必勝。
  • 對於先手來說,如果當前局面是 \(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;
}