首页 > 其他 > 详细

题解 P4301 [CQOI2013] 新Nim游戏

时间:2021-09-02 14:32:13      阅读:10      评论:0      收藏:0      [点我收藏+]

P4301 [CQOI2013] 新Nim游戏

先提一下 Nim 游戏:

有多堆石子,双方每次可以在一堆里取任意多的火柴,先手必胜当且仅当所有堆的石子数异或和等于 \(0\)

我们现在的目标就是拿走几堆火柴,让对面拿走多少堆火柴都没有办法使得异或和为 \(0\) (不能一次性拿完)。

或者说,我们要选出一些数组成一个集合,这里面的数不能互相异或和表示出来。我们就能想到线性基了。

我们求放进去的数的和最大的线性基。这样我们把其他的数取走就可以得到最小的方案。我们将火柴堆从大到小排序,动态维护一个线性基,每次试图向里面插入一个数,如果插入成功就将这个数统计到答案,并保留插入结果。最后再拿总数减去即可。

由于我们肯定有必胜策略(一次性拿得只剩一堆),所以 \(-1\) 肯定是没分的。

code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=1e3+10;

int n;
ll arr[N];

bool insert(ll x)
{
	for(int i=63;i>=0;i--)
	{
		if(!(x>>i&1)) continue;
		if(arr[i]) x^=arr[i];
		else
		{
			for(int j=0;j<i;j++)
				if(x>>j&1) x^=arr[i];
			for(int j=i+1;j<n;j++)
				if(arr[j]>>i&1) arr[j]^=x;
			arr[i]=x;
			return 1;
		}
	}
	return 0;
}
ll poi[N];

int main()
{
	scanf("%d",&n);
	ll sum=0;
	for(int i=1;i<=n;i++)
		scanf("%lld",&poi[i]),sum+=poi[i];
	sort(poi+1,poi+1+n,greater<long long>());
	for(int i=1;i<=n;i++)
		if(insert(poi[i])) sum-=poi[i];
	printf("%lld",sum);
}

题解 P4301 [CQOI2013] 新Nim游戏

原文:https://www.cnblogs.com/IzayoiMiku/p/15217520.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!