BZOJ 3759 Hungergame

首先我们不难看出如果存在一个异或和为0的子集,那么先手必胜,否则先手必败

证明如下:

1、首先如果至少存在一个异或和为0的子集,那么一定存在一个异或和为0的子集使得选取之后剩下的数的任意子集异或和不为0

2、假设我们已经选取了一个异或和为0的子集,无论后手怎么做,我们总是有办法使得当前选取的子集异或和为0

->因为后手无论是拿石子还是取石子之后,当前子集异或和不等于0,根据Nim游戏可知,此时先手一定有方案使得异或和为0

至此,我们证明了如果至少存在一个异或和为0的子集,先手必胜

那么我们可以知道

1、如果不存在一个异或和为0的子集,那么先手无论怎么选取,其选取后子集异或和不为0

2、由上述可知,后手一定有方案可以使得当前子集异或和为0,此时先手必败

所以我们证明了如果不存在一个异或和为0的子集,先手必败

 

那么我们就需要求是否存在一个子集异或和为0

首先数据范围很小,我们可以暴力

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<algorithm>
using namespace std;
 
typedef long long LL;
int T,n;
int Num[32];
bool f;
 
void DFS(int p,int tot,int now){
    if(p>n){
        if(tot&&now==0)f=true;
        return;
    }
    DFS(p+1,tot+1,now^Num[p]);
    DFS(p+1,tot,now);
}
 
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=1;i<=n;++i)scanf("%d",&Num[i]);
        f=false;
        DFS(1,0,0);
        if(f)printf("Yes\n");
        else printf("No\n");
    }return 0;
}
DFS

但是,这实质上是在求给定数组中是否存在一个线性相关组,高斯消元求线性基即可

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<iostream>
using namespace std;
 
bool f;
int T,n;
int Num[32],a[32];
 
bool check(){
    for(int i=1;i<=n;++i){
        for(int j=30;j>=0;--j){
            if(a[i]>>j&1){
                if(!Num[j]){Num[j]=a[i];break;}
                else{a[i]^=Num[j];}
            }
        }
        if(!a[i])return true;
    }return false;
}
 
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        memset(Num,0,sizeof(Num));
        f=false;
        for(int i=1;i<=n;++i)scanf("%d",&a[i]);
        if(check())printf("Yes\n");
        else printf("No\n");
    }return 0;
}
线性基

 

posted @ 2016-03-29 21:40  _Vertical  阅读(380)  评论(0编辑  收藏  举报