取(m堆)石子游戏 HDU - 2176 vj
题意概述:给出n堆石子,nim博弈判谁能赢,并且如果先手获胜输出所有先手获胜的第一步方案。
复习了一下(nim)博弈.
判赢的(nim)异或和(nim = a_{1} oplus a_{2} oplus a_{3} oplus a_{4}oplus cdots oplus a_{n})
如果(nim)和为(0)先手必败
否则,我们看这样一组数
0001
0010
0100
1000
其(nim)和为1111显然,所以先手必赢,那么先手如何赢?就是通过一个操作,让下一步该对面先手时,处于必败态。什么操作?就是让下一步对面面临着nim和为0,
首先要知道(a oplus b oplus a = b)所以可让(t = nim oplus a_{i})这样再判(t)是否小于(a_{i}),如果小于,那麽(t)一定可以从(a_{i})取出来,如果不能,说明这堆不能操作,得找其他堆进行操作
我还想胡乱证明一下为什么nim和大于0,一定能找到一种方式,使得该对方先手时,nim积为0。
因为(nim)一定存在一个最高位,这是必然的,那么造成(nim)的这个最高位(a),他一定是也有这个最高位的,如果(t = nim oplus a_{i})那么(t)一定小于(a),也就是一定存在方案。
#include <iostream>
using namespace std;
int cas = 0;
int m;
int a[200099];
void solve() {
for (int i = 1; i <= m; i++)
cin >> a[i];
int nim = 0;
for (int i = 1; i <= m; i++)
nim ^= a[i];
if (nim == 0) {
cout << "No
";
} else {
cout << "Yes
";
for (int i = 1; i <= m; i++) {
int Nim = nim^a[i];
if (Nim < a[i]) {
cout << a[i] << " " << Nim << endl;
}
}
}
}
int main()
{
while (cin >> m) {
if (m == 0)return 0;
solve();
}
}