[BZOJ3105] [cqoi2013]新Nim游戏
[BZOJ3105] [cqoi2013]新Nim游戏
Description
传统的Nim游戏是这样的:有一些火柴堆,每堆都有若干根火柴(不同堆的火柴数量可以不同)。两个游戏者轮流操作,每次可以选一个火柴堆拿走若干根火柴。可以只拿一根,也可以拿走整堆火柴,但不能同时从超过一堆火柴中拿。拿走最后一根火柴的游戏者胜利。本题的游戏稍微有些不同:在第一个回合中,第一个游戏者可以直接拿走若干个整堆的火柴。可以一堆都不拿,但不可以全部拿走。第二回合也一样,第二个游戏者也有这样一次机会。从第三个回合(又轮到第一个游戏者)开始,规则和Nim游戏一样。如果你先拿,怎样才能保证获胜?如果可以获胜的话,还要让第一回合拿的火柴总数尽量小。
Input
第一行为整数k。即火柴堆数。第二行包含k个不超过109的正整数,即各堆的火柴个数。
Output
输出第一回合拿的火柴数目的最小值。如果不能保证取胜,输出-1。
Sample Input
6
5 5 6 6 5 5
Sample Output
21
HINT
k<=100
试题分析
仔细分析,我们要留下极大的组使得组内没有一个元素能被其它元素异或得出,否则第二个人取到只剩下这几个元素先手就输了。
极大的线性无关组,线性基正好能满足这些性质。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
//#include<cmath>
#include<algorithm>
using namespace std;
#define LL long long
inline LL read(){
LL x=0,f=1; char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
return x*f;
}
const LL INF = 2147483600;
const LL MAXN = 100010;
LL N,ans; LL v[MAXN+1];
LL a[MAXN+1];
inline void Gauss(LL x){
LL y=x;
for(LL i=31;i>=0;i--){
if(x&(1LL<<i)){
if(!v[i]) {v[i]=x; break;}
else x=x^v[i];
} if(!x) return ;
} if(x) ans-=y;
}
bool cmp(LL x,LL y){return x>y;}
int main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
N=read();
for(LL i=1;i<=N;i++){
a[i]=read(); ans+=a[i];
} sort(a+1,a+N+1,cmp);
for(LL i=1;i<=N;i++) Gauss(a[i]);
printf("%lld\n",ans);
return 0;
}
你——悟到了么?