洛谷P4301 [CQOI2013]新Nim游戏

洛谷P4301 [CQOI2013]新Nim游戏

题目描述

传统的Nim游戏是这样的:有一些火柴堆,每堆都有若干根火柴(不同堆的火柴数量可以不同)。两个游戏者轮流操作,每次可以选一个火柴堆拿走若干根火柴。可以只拿一根,也可以拿走整堆火柴,但不能同时从超过一堆火柴中拿。拿走最后一根火柴的游戏者胜利。

本题的游戏稍微有些不同:在第一个回合中,第一个游戏者可以直接拿走若干个整堆的火柴。可以一堆都不拿,但不可以全部拿走。第二回合也一样,第二个游戏者也有这样一次机会。从第三个回合(又轮到第一个游戏者)开始,规则和Nim游戏一样。

如果你先拿,怎样才能保证获胜?如果可以获胜的话,还要让第一回合拿的火柴总数尽量小。

输入输出格式

输入格式:

第一行为整数\(k\)。即火柴堆数。
第二行包含\(k\)个不超过10^9的正整数,即各堆的火柴个数。

输出格式:

输出第一回合拿的火柴数目的最小值。如果不能保证取胜,输出-1

思路:

线性基
对于nim游戏,有一个规律就是如果所有数异或和不为零,那么就是先手胜,否则先手败。
可以这么理解这个规律,如果异或和不为零,那么说明此时棋盘上还有棋子,那么先手一定可以通过一次操作使得异或和变成零。
当异或和变成零之后,就会有两种情况,一种是棋盘上还有棋子,一种是棋盘上没有棋子。
对于第二种情况,已经获得胜利了。
而对于第一种情况,后手的一次操作一定会让异或和非零,那么就再次回到了一开始的情况,如此重复,先手一定会赢。

那么对于新nim游戏,留给先手的问题就变成了构造出一个和最大的数集,使这个数集中全部或部分的数以任意方式组合进行异或运算都无法使得异或和为零,即后手无法构造出异或和为零的状态(他的必胜状态)。

至此就符合了线性基性质。

CODE:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAXN 110
int nums[MAXN],li[MAXN];
int i,j,k,m,n;
long long int ans;
bool insert(int n){
	for(int i=62;i>=0;i--){
		if((n>>i)&1){
			if(!li[i]){
				li[i]=n;
				return true;
			}
			n^=li[i];
		}
	}
	return false;
}
int main(){
	scanf("%d",&n);
	for(i=1;i<=n;i++) scanf("%d",nums+i);
	sort(nums+1,nums+n+1);
	for(i=n;i>=1;i--){
		if(!insert(nums[i])) ans+=nums[i];
	}
	printf("%lld\n",ans);
	return 0;
}
posted @ 2018-12-29 21:52  EternalBlue  阅读(143)  评论(0编辑  收藏  举报