本题是博弈题,但是用到了线性基。
首先来描述下博弈的部分,总共会有三个状态:赢,平,输。三者的权重高低从大到小,假设一方的下一个动作导致自己进入失败的状态,那么他可以通过复读上一个人的行为,来使集合元素不再增加。因此只有当第一次alice能赢或者alice无论选了哪两个数进行异或,bob选数都能与之异或达到k这两种情况有用,bob选数其中一个必须是alice异或产生的数字。
第一个情况很好表现,只要set存一下就行了。时间是nlogn
第二个如果你用暴力加二分寻找,那么时间复杂度会是
,必定超时。
那么这里就要用到线性基。通过线性基的封闭性来求得。假设,那么很显然
,原序列任意两个数的异或都可以由线性基得到,线性基的数量是固定的,是极大线性无关组,线性基能表示的数量为 2的线性基个数次方。两个元素异或的并集是线性基表示的数字的子集,同时也要是原来集合的子集。因此需要原来序列大小等于2的线性基个数大小。
要经过去重操作,重复的数字加入线性基不会再增加线性基个数。
有一说一我真的没搜到线性基的封闭性是什么东西,这也是第一次看到,如果有更深的理解之后再来修改。
#include<bits/stdc++.h>
#define ll long long
#define N 1000005
#define M 105
#define debug cout<<"~~debug "
#define rc p<<1|1
#define lc p<<1
#define PII pair<long long,long long>
#define pb push_back
#define Inf 0x3f3f3f3f
//neuro-sama
const ll mod = 1e9+7;
using namespace std;
inline int read()
{
char c=getchar();int f=1,x=0;
while(c>'9'||c<'0'){ if(c=='-') f=-f;c=getchar();}
while(c<='9'&&c>='0'){x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return f*x;
}
int d[35],a[N];
int T,n,k;
void add(int x){//线性基的获取
for(int i=31;i>=0;i--){
if(1&(x>>i)){
if(!d[i]){
d[i]=x;
break;
}
else x^=d[i];
}
}
}
void solve()
{
memset(d,0,sizeof(d));
cin>>n>>k;
set<int>s;
for(int i=0;i<n;i++){
cin>>a[i];
s.insert(a[i]^k);//set存入
}
for(int i=0;i<n;i++){
if(s.count(a[i])){
cout<<"Alice"<<endl;
return;
}
}
sort(a,a+n);
n=unique(a,a+n)-a;//去重
for(int i=0;i<n;i++){
a[i]^=k;
add(a[i]);
}
int cnt=0;
for(int i=32;i>=0;i--){
if(d[i]) cnt++;
}
if((1<<cnt)==n){
cout<<"Bob"<<endl;
}
else cout<<"Draw"<<endl;
}
int main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
T=1;
cin>>T;
while(T--)
{
solve();
}
}