题意:先介绍了尼姆博弈,尼姆博弈当给出的序列异或等0时先手输。现在为了让后手赢,在游戏开始前后手可以移除一些堆。必须整堆的移除,不能移除部分,并且不能把全部的堆移出去。问移除之后后手能否赢,可以输出“Yes”,否则输出“No”。
解题思路:根据题目可以知道,当移除某些数之后剩下的数异或等0。利用高斯消元,将n个数ai作为行,每个数的二进制位作为列,这里可以构造出一个01矩阵进行高斯消元。
消元之后如果存在全0行则输出Yes,否则输出No。
因为ai最大1e12所以最大有40位,即列的范围最大取40。所以当n>40时一定存在全0行,直接输出Yes。
小于40的用高斯消元直接判断(a[i]的范围会爆int ,当时做题时没注意,WA了好几发)。
AC代码:
#include <iostream>
#include <stdio.h>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
typedef long long ll;
const int maxn=1050;
ll a[maxn];
void gauss(int n,int var)
{
int r,c,tmp_r;///r代表当前遍历的行,c代表当前遍历的列
///tmp_r枚举r行以下的行
r=0,c=0;
for(r=0,c=0;r<n&&c<var;r++,c++)
{
for(tmp_r=r;tmp_r<n;tmp_r++)///找出二进制第c的位置不为0的数
{
if(a[tmp_r]&(1LL<<c)) break;
}
if(tmp_r==n)///如果第c位置全部为0,则当前行不向下枚举,
{
r--;
continue;
}
swap(a[r],a[tmp_r]);///找到不为零的数与当前行交换
for(int i=r+1;i<n;i++)///置0,形成上三角
{
if(a[i]&(1LL<<c))
a[i]=a[i]^a[r];
}
}
if(n-r>0)///如果矩阵的秩小于n则输出Yes
printf("Yes\n");
else
printf("No\n");
}
int main()
{
int n,T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=0;i<n;i++)///存储数据
scanf("%I64d",&a[i]);
if(n>40)
printf("Yes\n");
else
gauss(n,40);
}
return 0;
}