xsy2468. Game
\(\text{Alice}\) 和 \(\text{Bob}\) 在一棵 \(n\) 个节点的树上玩游戏,每个节点初始要么为黑色要么为白色。
\(\text{Alice}\) 先手,轮流选择一个白色点 v,将路径 \((1,v)\) 全部染成黑色。最后不能操作的人为输。
计算 \(\text{Alice}\) 是否必胜以及所有必胜可能的第一步节点的选择。
\(1\le n\le 10^5\)。
考虑一条路径 \((1,v)\) 被染黑后的局面相当于被分成若干个子游戏,可以想到用 \(\text{SG}\) 函数来处理。
设 \(dp_{u}\) 表示以 \(u\) 为根的子树的 \(\text{SG}\) 函数值,\(f_{u,i}\) 表示以 \(u\) 为根的子树内,操作 \((u,i)\) 后的 \(\text{SG}\) 函数值,\(sum_{u}=\bigoplus\limits_{i\in \text{son}_{u}} dp_{i}\),则有
\[\left\{\begin{matrix}
f_{u,u}=sum_{u}\\
f_{u,i}=dp_{v}\oplus sum_{u}\oplus f_{v,i}\quad (v\in \text{son}_{u}\cap i\in \text{subtree}_{v})\\
dp_{u}=\text{mex}\{f_{u,i}\mid(i\in \text{subtree}_{u})\}\\
\end{matrix}\right.
\]
\(O(n^2)\) 可以直接求出这些值。
如何加速?这里需要一个数据结构来支持以下操作:
- 求出子树内所有 \(f\) 的 \(\text{mex}\)。
- 将数据结构内的数据都异或上 \(x\)。
- 将子树内的所有 \(f\) 并到当前点。
可以用 \(\text{01trie}\) 来进行这些操作,合并类似于线段树合并即可,总时间复杂度 \(O(n\log n)\)。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5,L=16;
struct Node{int sz,son[2],tag;}tree[N<<2];
int n,flag,tot,col[N],suc[N],sum[N],dp[N],rt[N];vector<int>G[N];
inline void pushup(int x){
tree[x].sz=tree[tree[x].son[0]].sz+tree[tree[x].son[1]].sz;
}
inline void pushdown(int x,int at){
if(tree[x].tag>>at&1)swap(tree[x].son[0],tree[x].son[1]);
tree[x].tag&=((1<<at)-1);
tree[tree[x].son[0]].tag^=tree[x].tag;
tree[tree[x].son[1]].tag^=tree[x].tag;
tree[x].tag=0;
}
inline void insert(int &x,int at){
if(!x)x=++tot;
if(!~at){tree[x].sz=1;return;}
pushdown(x,at);
insert(tree[x].son[0],at-1);
pushup(x);
}
inline int merge(int rx,int ry,int at){
if(!rx||!ry)return rx+ry;
pushdown(rx,at),pushdown(ry,at);
if(!~at)return rx;
tree[rx].son[0]=merge(tree[rx].son[0],tree[ry].son[0],at-1);
tree[rx].son[1]=merge(tree[rx].son[1],tree[ry].son[1],at-1);
return pushup(rx),rx;
}
inline int query_mex(int x,int ans,int at){
if(!~at)return ans;
pushdown(x,at);
if(tree[tree[x].son[0]].sz<(1<<at))return query_mex(tree[x].son[0],ans,at-1);
return query_mex(tree[x].son[1],ans|(1<<at),at-1);
}
inline void op_xor(int x,int t){tree[x].tag^=t;}
inline void dfs(int x,int fa){
int fl=0;
for(auto y:G[x])if(y^fa){
fl=1,dfs(y,x),sum[x]^=dp[y];
rt[x]=merge(rt[x],rt[y],L);
}
if(fl){
if(!col[x])insert(rt[x],L);
op_xor(rt[x],sum[x]);
dp[x]=query_mex(rt[x],0,L);
op_xor(rt[x],dp[x]);
}
else if(!col[x])dp[x]=1,insert(rt[x],L),op_xor(rt[x],dp[x]);
}
inline void solve(int x,int fa,int val){
if(!val&&!col[x])suc[x]=1;
for(auto y:G[x])if(y^fa)solve(y,x,val^sum[y]^dp[y]);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i)scanf("%d",col+i);
for(int i=1,x,y;i<n;++i){
scanf("%d%d",&x,&y);
G[x].emplace_back(y),G[y].emplace_back(x);
}
dfs(1,0),solve(1,0,sum[1]);
for(int i=1;i<=n;++i)if(suc[i])printf("%d\n",i),flag=1;
if(!flag)puts("-1");
return 0;
}