luogu P6665 [清华集训2016] Alice 和 Bob 又在玩游戏
题面传送门
首先你必须了解的是SG定理,设\(f_S\)为\(S\)状态对应的SG值,则设\(S\)可以到达的状态为\(T\),则\(f_S=\operatorname{mex}{f_T}\)。
如果\(S\)能被划分成若干个互不影响的状态,记为\(S1,S2,\dots,S_k\),则\(f_i\)为所有\(f_{S_j}\)的异或和。
如果\(f_i\)为\(0\)则先手必败,反之必胜。
那么这个题就变成在子树内枚举一个点,然后把这个点到根的路径全删了,剩下的子树的SG值异或起来,最后求mex。
然后发现这个东西其实可以合并子树,只要全局异或上一个东西就好了,Trie随便维护。
时间复杂度\(O(n\log n)\)
code:
#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define re register
#define RI re int
#define ll long long
#define db double
#define lb long db
#define N (100000+5)
#define M (100000)
#define mod 998244353
#define Mod (mod-1)
#define eps (1e-9)
#define U unsigned int
#define it iterator
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) (n*(x-1)+(y))
#define R(n) (rand()*rand()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using namespace std;
int x,y,T,n,m,Ans,Ro[N],dp[N],vis[N];vector<int> S[N];
namespace Trie{
int cnt,F[N*20],L[N*20],R[N*20],Fl[N*20],Val[N*20];I void Up(int now){F[now]=F[L[now]]+F[R[now]];}
I void PF(int now,int d,int w){now&&(Fl[now]^=w,w>>d&1&&(swap(L[now],R[now]),0));}I void P(int now,int d){Fl[now]&&(PF(L[now],d-1,Fl[now]),PF(R[now],d-1,Fl[now]),Fl[now]=0);}
I void Ins(int x,int &now,int d=18){!now&&(now=++cnt);if(d==-1){Val[now]++;F[now]=1;return;}P(now,d);x>>d&1?Ins(x,R[now],d-1):Ins(x,L[now],d-1);Up(now);}
I int ME(int x,int y,int d=18){if(!x||!y) return x|y;if(d==-1){Val[x]+=Val[y];return x;}P(x,d);P(y,d);L[x]=ME(L[x],L[y],d-1);R[x]=ME(R[x],R[y],d-1);Up(x);return x;}
I int Qry(int now,int d=18){if(d==-1) return 0;P(now,d);return F[L[now]]^(1<<d)?Qry(L[now],d-1):Qry(R[now],d-1)|(1<<d);}
}
I void calc(int x,int La){vis[x]=1;RI Ns=0;for(RI i:S[x]) i^La&&(calc(i,x),Ns^=dp[i]);Trie::Ins(Ns,Ro[x]);for(RI i:S[x]) i^La&&(Trie::PF(Ro[i],18,Ns^dp[i]),Ro[x]=Trie::ME(Ro[x],Ro[i]));dp[x]=Trie::Qry(Ro[x]);}
I void Solve(){
RI i;scanf("%d%d",&n,&m);for(i=1;i<=m;i++) scanf("%d%d",&x,&y),S[x].PB(y),S[y].PB(x);Ans=0;for(i=1;i<=n;i++) !vis[i]&&(calc(i,0),Ans^=dp[i]);
puts(Ans?"Alice":"Bob");for(i=1;i<=n;i++) vis[i]=dp[i]=Ro[i]=0,S[i].clear();for(i=1;i<=Trie::cnt;i++) Trie::F[i]=Trie::Fl[i]=Trie::L[i]=Trie::R[i]=0;Trie::cnt=0;
}
int main(){
freopen("1.in","r",stdin);
scanf("%d",&T);while(T--)Solve();
}