[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;
}


posted @ 2018-08-27 11:12  wxjor  阅读(125)  评论(0编辑  收藏  举报