首先我们不难看出如果存在一个异或和为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 "); else printf("No "); }return 0; }
但是,这实质上是在求给定数组中是否存在一个线性相关组,高斯消元求线性基即可
#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 "); else printf("No "); }return 0; }