[BZOJ3105][CQOI2013]新Nim游戏

bzoj
luogu

sol

根据Nim游戏的原理可知若异或和为零则后手必胜,否则先手必胜。
所以题目的要求其实就是:选出一个总和尽量小的集合,使其补集的任意非空子集队异或和都不为零。
转化一下,去掉一个总和尽量小的集合,也就是保留尽量大。
这也就是bzoj2460元素那个题了。
而且易证答案不可能为-1:只要第一回合拿到只剩一堆石子,那么第二回合对手就不能操作。这样第三回合就可以拿走剩下的一整堆然后取胜了。

code

#include<cstdio>
#include<algorithm>
using namespace std;
int gi()
{
	int x=0,w=1;char ch=getchar();
	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if (ch=='-') w=0,ch=getchar();
	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return w?x:-x;
}
const int N = 1e3+5;
int n,a[N];long long ans;
struct xxj{
	int p[32];
	void insert(int x)
		{
			for (int j=30;~j;--j)
			{
				if (!(x>>j)) continue;
				if (!p[j]) {p[j]=x;return;}
				x^=p[j];
			}
		}
	int query(int x)
		{
			for (int j=30;~j;--j)
				x=min(x,x^p[j]);
			return x;
		}
}S;
int main()
{
	n=gi();
	for (int i=1;i<=n;++i) a[i]=gi();
	sort(a+1,a+n+1);
	for (int i=n;i;--i)
		if (S.query(a[i])) S.insert(a[i]);
		else ans+=a[i];
	printf("%lld\n",ans);
	return 0;
}
posted @ 2018-03-27 14:04  租酥雨  阅读(160)  评论(0编辑  收藏  举报