n居然是10w左右你敢信。。。连爆10+发OJ才发现。
首先给每一个子树一个sg值;考虑当前点所在子树的sg,枚举第一次删哪一个点,那么剩下一些子树,这些子树的xor就是这个后继的值,然后求mex即可。
考虑用线段树合并来维护。用线段树维护某一个要被删除的点在当前点的后继的值,那么更新就相当于全部抑或一个值,打标记维护即可。
查询就贪心向左走即可。
AC代码如下:
#include<bits/stdc++.h>
#define N 200005
#define M 4000005
#define clr(x,y) memset(x,0,sizeof(int)*(y+1))
using namespace std;
int n,m,tot,trtot,bin[25],fst[N],pnt[N],nxt[N],fa[N],sg[N],rt[N],ls[M],rs[M],sz[M],tg[M];
bool vis[N];
void add(int x,int y){
pnt[++tot]=y; nxt[tot]=fst[x]; fst[x]=tot;
}
void pushdn(int x,int k){
if (tg[x]){
if (tg[x]&bin[k-1]) swap(ls[x],rs[x]);
tg[ls[x]]^=tg[x]; tg[rs[x]]^=tg[x]; tg[x]=0;
}
}
void ins(int &k,int x,int y){
sz[k=++trtot]=1; if (!y) return;
if (x&bin[y-1]) ins(rs[k],x,y-1); else ins(ls[k],x,y-1);
}
int mrg(int x,int y,int k){
if (!x || !y) return x|y;
pushdn(x,k); pushdn(y,k);
ls[x]=mrg(ls[x],ls[y],k-1); rs[x]=mrg(rs[x],rs[y],k-1);
sz[x]=sz[ls[x]]+sz[rs[x]]+(k?0:1); return x;
}
void dfs(int x){
int i,y,t=0; vis[x]=1;
for (i=fst[x]; i; i=nxt[i]){
y=pnt[i];
if (y!=fa[x]){
fa[y]=x; dfs(y); t^=sg[y];
}
}
ins(rt[x],t,19);
for (i=fst[x]; i; i=nxt[i]){
y=pnt[i];
if (y!=fa[x]){
tg[rt[y]]=t^sg[y]; rt[x]=mrg(rt[x],rt[y],19);
}
}
for (i=19,y=rt[x]; i; i--){
pushdn(y,i);
if (sz[ls[y]]<bin[i-1]) y=ls[y];
else{
sg[x]|=bin[i-1]; y=rs[y];
}
}
}
int main(){
int cas,i; scanf("%d",&cas);
bin[0]=1; for (i=1; i<=19; i++) bin[i]=bin[i-1]<<1;
while (cas--){
scanf("%d%d",&n,&m); tot=trtot=0;
if (n>200000){return 0;}
int x,y;
for (i=1; i<=m; i++){
scanf("%d%d",&x,&y);
add(x,y); add(y,x);
}
int ans=0;
for (i=1; i<=n; i++)
if (!vis[i]){
dfs(i); ans^=sg[i];
}
puts((ans)?"Alice":"Bob");
if (cas){
memset(vis,0,sizeof(bool)*(n+1));
clr(sg,n); clr(fa,n); clr(fst,n); clr(rt,n);
clr(ls,trtot); clr(rs,trtot); clr(sz,trtot); clr(tg,trtot);
}
}
return 0;
}
by lych
2016.12.31