Solution 取火柴游戏
题目大意:(Nim) 游戏输方案
博弈论,(Nim) 游戏
分析:
首先(Nim)和定理,(Nim)游戏存在先手必胜状态,当且仅当(a_1 igoplus a_2 igoplus a_3 dots igoplus a_n eq 0)
分析,首先最终状态所有物品取完(Nim)和显然为(0),为必败状态,反过来对手就是必胜状态了
对于(a_1 igoplus a_2 igoplus a_3 dots igoplus a_n eq 0),我们假设(a_1 igoplus a_2 igoplus a_3 dots igoplus a_n=k),我们任选一个(a_i)将其异或上(k)即可
根据定义,一定有奇数个(a_i)最高位和(k)最高位相同,因此异或后比原来小,是个合法操作
对于(a_1 igoplus a_2 igoplus a_3 dots igoplus a_n = 0),一定不存在一种方案使得操作后仍有(a_1 igoplus a_2 igoplus a_3 dots igoplus a_n = 0),因为这样操作后两数相等不是合法操作,也就是说
(a_1 igoplus a_2 igoplus a_3 dots igoplus a_n eq 0)时是必胜状态,它可以转移到
(a_1 igoplus a_2 igoplus a_3 dots igoplus a_n = 0)是必败状态
而必败状态只能转移到必胜状态,根据归纳法得证
再来看输方案,我们枚举每一个(a_i)检查异或上(k)后是否合法即可
#include <cstdio>
#include <cctype>
using namespace std;
const int maxn = 5e5 + 100;
inline int read(){
int x = 0;char c = getchar();
while(!isdigit(c))c = getchar();
while(isdigit(c))x = x * 10 + c - '0',c = getchar();
return x;
}
int val[maxn],n,sum,ansa,ansb;
int main(){
n = read();
for(int i = 1;i <= n;i++)
sum ^= val[i] = read();
if(sum == 0)return puts("lose"),0;
for(int i = 1;i <= n;i++){
int target = sum ^ val[i];
if(target > val[i])continue;
ansa = val[i] - target;
ansb = i;
break;
}
printf("%d %d
",ansa,ansb);
val[ansb] -= ansa;
for(int i = 1;i <= n;i++)
printf("%d%c",val[i],i == n ? '
' : ' ');
return 0;
}