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;
}
posted @ 2022-06-08 19:30  Samsara-soul  阅读(25)  评论(0编辑  收藏  举报