题目大意
给定一棵树,$1$号节点为根,两个人轮流操作,每次选择一个根节点外的点,删掉它以及它的子树,不能操作者输,求两人均采用最优策略下先手胜利还是后手胜利。
题解
经典问题树上删边游戏,根据论文算法合集之《组合游戏略述——浅谈SG游戏的若干拓展及变形》,每个节点的$SG$值为其所有子节点$SG$值$+1$的异或和。
判一号节点的$SG$值是否为$0$即可。
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<cmath> #define LL long long #define M 200020 using namespace std; namespace IO{ const int BS=(1<<20); int Top=0; char Buffer[BS],OT[BS],*OS=OT,*HD,*TL,SS[20]; const char *fin=OT+BS-1; char Getchar(){if(HD==TL){TL=(HD=Buffer)+fread(Buffer,1,BS,stdin);} return (HD==TL)?EOF:*HD++;} void flush(){fwrite(OT,1,OS-OT,stdout);} void Putchar(char c){*OS++ =c;if(OS==fin)flush(),OS=OT;} void write(int x){ if(!x){Putchar('0');return;} if(x<0) x=-x,Putchar('-'); while(x) SS[++Top]=x%10,x/=10; while(Top) Putchar(SS[Top]+'0'),--Top; } int read(){ int nm=0,fh=1; char cw=Getchar(); for(;!isdigit(cw);cw=Getchar()) if(cw=='-') fh=-fh; for(;isdigit(cw);cw=Getchar()) nm=nm*10+(cw-'0'); return nm*fh; } } using namespace IO; int n,fs[M],nt[M],to[M],tmp; void link(int x,int y){nt[tmp]=fs[x],fs[x]=tmp,to[tmp++]=y;} int DP(int x,int last){ int sg=0; for(int i=fs[x];i!=-1;i=nt[i]) if(to[i]!=last) sg^=(DP(to[i],x)+1); return sg; } int main(){ n=read(),memset(fs,-1,sizeof(fs)); for(int i=1;i<n;i++){int x=read(),y=read();link(x,y),link(y,x);} puts(DP(1,0)?"Alice":"Bob"); return 0; }