【题解】取火柴游戏

Link

\(\text{Solution:}\)

回顾一下\(Nim\)游戏那个优美结论的\(\text{conclusion:}\)

将所有石子数量异或起来,若和为\(0\)则必败,否则必胜。(先手)

那分以下情况考虑:

首先是全\(0:\)此时必败,显然异或和为\(0\).

然后是异或和不为\(0\)的状态:那我们必定可以找到一个\(a_i\),将它改变,使得异或和为\(0\).

因为二进制异或下的某一位是\(1\)的话则一定有奇数个数在此处是\(1\).那么我们随意找一个,令异或和为\(k\).将\(a_i\to a_i\text{^} k\)则这个数必然比原来的\(a_i\)小。因为最高位改变了。

第三种情况是异或和为\(0\)时不存在一种移动使得下一步的异或和依旧是\(0\).

因为如果让\(a_i\to a_i*\)成立的话,则必然有\(a_i*=a_i\),是一个不合条件的移动。

那么对于这个题:第一步用\(Nim\)和判断是不是必败,第二步枚举每一个队看看能不能修改即可。

#include<bits/stdc++.h>
using namespace std;
int n,a[5000010],s;
pair<int,int>p;
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i)scanf("%d",a+i);
	for(int i=1;i<=n;++i)s^=a[i];
	if(s==0){
		puts("lose");
		return 0;
	}
	for(int i=1;i<=n;++i){
		int S=s;
		S^=a[i];
		if(a[i]>=S){
			p.first=a[i]-S;
			p.second=i;
			a[i]-=p.first;
			break;
		}
	}
	printf("%d %d\n",p.first,p.second);
	for(int i=1;i<=n;++i)printf("%d ",a[i]);
	puts("");
	return 0;
}
posted @ 2020-06-07 09:11  Refined_heart  阅读(199)  评论(0编辑  收藏  举报