Forever Young

洛谷 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;
}
posted @ 2020-09-10 10:53  Loceaner  阅读(162)  评论(2编辑  收藏  举报