洛谷 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;
}
转载不必联系作者,但请声明出处