每棵树单独考虑
对于每个子树,通过枚举删去的点,可以算出剩下的所有树的SG值的异或值的集合,集合的MEX就是这个子树的SG值
用字典树维护所有情况,合并的时候打个标记,然后字典树合并就行了
#include
#include
#include
#include
using namespace std;
const int N=100010;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x){
char c=nc(); x=0;
for(;c>'9'||c<'0';c=nc());for(;c>='0'&&c<='9';x=x*10+c-'0',c=nc());
}
int t,n,m,cnt,cnt0,G[N],sg[N],vis[N],rt[N],tag[N*50],size[N*50],ch[N*50][2];
struct edge{
int t,nx;
}E[N<<1];
inline void addedge(int x,int y){
E[++cnt].t=y; E[cnt].nx=G[x]; G[x]=cnt;
E[++cnt].t=x; E[cnt].nx=G[y]; G[y]=cnt;
}
int Rev(int x){
int ret=0;
for(int i=0;i<=20;i++) ret=(ret<<1)|(x>>i&1);
return ret;
}
inline void Push(int x){
if(tag[x]&1)
swap(ch[x][0],ch[x][1]);
tag[ch[x][0]]^=tag[x]>>1,tag[ch[x][1]]^=tag[x]>>1;
tag[x]=0;
}
void Up(int x){
size[x]=size[ch[x][0]]+size[ch[x][1]]+1;
}
int Merge(int x,int y){
if(!x || !y) return x+y;
Push(x); Push(y);
ch[x][0]=Merge(ch[x][0],ch[y][0]);
ch[x][1]=Merge(ch[x][1],ch[y][1]);
Up(x);
return x;
}
void Insert(int x,int y,int k=20){
if(k<0) return Up(x); Push(x);
if(!ch[x][y>>k&1]) ch[x][y>>k&1]=++cnt0;
Insert(ch[x][y>>k&1],y,k-1);
Up(x);
}
int Query(int x){
int ret=0;
for(int i=20;~i;i--){
Push(x);
if(size[ch[x][0]]==(1<
ret|=1<
else
x=ch[x][0];
}
return ret;
}
void dfs(int x,int f){
rt[x]=++cnt0; size[cnt0]=1; vis[x]=1;
int sum=0;
for(int i=G[x];i;i=E[i].nx)
if(E[i].t!=f) dfs(E[i].t,x),sum^=sg[E[i].t];
for(int i=G[x];i;i=E[i].nx)
if(E[i].t!=f){
tag[rt[E[i].t]]^=Rev(sum^sg[E[i].t]);
int k=Query(rt[E[i].t]);
rt[x]=Merge(rt[x],rt[E[i].t]);
}
Insert(rt[x],sum);
sg[x]=Query(rt[x]);
}
int main(){
read(t);
while(t--){
read(n); read(m); cnt=cnt0=0; int ans=0;
memset(ch,0,sizeof(ch));
memset(tag,0,sizeof(tag));
memset(size,0,sizeof(size));
for(int i=1;i<=n;i++) vis[i]=G[i]=0;
for(int i=1,x,y;i<=m;i++)
read(x),read(y),addedge(x,y);
for(int i=1;i<=n;i++)
if(!vis[i]) dfs(i,0),ans^=sg[i];
puts(ans?"Alice":"Bob");
}
return 0;
}