colazcy

题目链接

Solution 取火柴游戏

题目大意:\(Nim\) 游戏输方案

博弈论,\(Nim\) 游戏


分析:

首先\(Nim\)和定理,\(Nim\)游戏存在先手必胜状态,当且仅当\(a_1 \bigoplus a_2 \bigoplus a_3 \dots \bigoplus a_n \neq 0\)

分析,首先最终状态所有物品取完\(Nim\)和显然为\(0\),为必败状态,反过来对手就是必胜状态了

对于\(a_1 \bigoplus a_2 \bigoplus a_3 \dots \bigoplus a_n \neq 0\),我们假设\(a_1 \bigoplus a_2 \bigoplus a_3 \dots \bigoplus a_n=k\),我们任选一个\(a_i\)将其异或上\(k\)即可

根据定义,一定有奇数个\(a_i\)最高位和\(k\)最高位相同,因此异或后比原来小,是个合法操作

对于\(a_1 \bigoplus a_2 \bigoplus a_3 \dots \bigoplus a_n = 0\),一定不存在一种方案使得操作后仍有\(a_1 \bigoplus a_2 \bigoplus a_3 \dots \bigoplus a_n = 0\),因为这样操作后两数相等不是合法操作,也就是说

\(a_1 \bigoplus a_2 \bigoplus a_3 \dots \bigoplus a_n \neq 0\)时是必胜状态,它可以转移到

\(a_1 \bigoplus a_2 \bigoplus a_3 \dots \bigoplus 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\n",ansa,ansb);
	val[ansb] -= ansa;
	for(int i = 1;i <= n;i++)
		printf("%d%c",val[i],i == n ? \'\n\' : \' \');
	return 0;
}

分类:

技术点:

相关文章: