题目分析
首先,我们删掉所有被Hack的点,剩下的点的父亲,为原树上它的第一个没有被Hack的祖先。则产生了一个森林。
那么对于这个游戏局面,sg值为每棵树的sg值的异或和。
现在考虑一棵树的sg怎么算。记 g ( x ) g(x) g(x)为在这棵树中选一个节点 x x x,将 x x x与其祖先全部删除,剩下的子树们的sg异或和。那么该树中所有 g ( x ) g(x) g(x)的mex就是该树的sg。
我们发现,假设我们现在在处理子树 x x x,处理子树 y y y( y y y为 x x x的父亲)的时候,子树 x x x中所有节点的 g ( x ) g(x) g(x)都要异或 y y y除了 x x x以外的儿子们的sg值。于是考虑用trie树来维护 g ( x ) g(x) g(x),异或就只要打标记就可以了。
然后做trie树合并获得 y y y的trie树,我们知道trie树合并的复杂度是 O ( n log n ) O(n\log n) O(nlogn)的。
再插入所有 y y y的所有儿子异或和,即 g ( y ) g(y) g(y)。
求出整棵trie树的mex,方法是记录trie树中以每个节点为根的子树是否是满的,如果是当前节点的左子树是满的,则在右子树中寻找这个mex值,否则在左子树中找。(往左走的边是0,往右走的边是1)
为了获得答案,我们还要记录每个 g ( x ) g(x) g(x)值对应的 x x x,这个只要对于trie树上的每个叶子节点开一个链表即可。
代码
#include<bits/stdc++.h> using namespace std; #define RI register int int read() { int q=0;char ch=' '; while(ch<'0'||ch>'9') ch=getchar(); while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar(); return q; } const int N=100005,mxd=25; int c[N],h[N],ne[N<<1],to[N<<1],fa[N],sg[N],st[N]; int bin[35],s[N*25][2],full[N*25],rt[N],tag[N*25],fir[N*25],nxt[N],tail[N*25]; int n,tot,ans,SZ,top,debug; void puttag(int x,int num,int d) { if(num&bin[d]) swap(s[x][0],s[x][1]); tag[x]^=num; } void pd(int x,int d) { if(s[x][0]) puttag(s[x][0],tag[x],d-1); if(s[x][1]) puttag(s[x][1],tag[x],d-1); tag[x]=0; } void up(int x) {full[x]=full[s[x][0]]&full[s[x][1]];} int merge(int x,int y,int d) { if(!x||!y) return x|y; if(d<0) { nxt[tail[x]]=fir[y],tail[x]=tail[y]; return x; } if(tag[x]) pd(x,d); if(tag[y]) pd(y,d); s[x][0]=merge(s[x][0],s[y][0],d-1); s[x][1]=merge(s[x][1],s[y][1],d-1); up(x);return x; } void ins(int &x,int d,int num,int id) { if(!x) x=++SZ; if(d<0) { full[x]=1; if(!fir[x]) fir[x]=tail[x]=id; else nxt[tail[x]]=id,tail[x]=id; return; } if(tag[x]) pd(x,d); if(num&bin[d]) ins(s[x][1],d-1,num,id); else ins(s[x][0],d-1,num,id); up(x); } int mex(int x,int d) { if(d<0) return 0; if(tag[x]) pd(x,d); if(!full[s[x][0]]) return mex(s[x][0],d-1); else return bin[d]+mex(s[x][1],d-1); } void work(int x) { int sum=0; for(RI i=h[x];i;i=ne[i]) work(to[i]),sum^=sg[to[i]]; for(RI i=h[x];i;i=ne[i]) puttag(rt[to[i]],sum^sg[to[i]],mxd); for(RI i=h[x];i;i=ne[i]) rt[x]=merge(rt[x],rt[to[i]],mxd); ins(rt[x],mxd,sum,x),sg[x]=mex(rt[x],mxd); } void getans(int x,int d,int num) { if(d<0) { for(RI i=fir[x];i;i=nxt[i]) st[++top]=i; return; } if(tag[x]) pd(x,d); if(num&bin[d]) getans(s[x][1],d-1,num); else getans(s[x][0],d-1,num); } void add(int x,int y) {to[++tot]=y,ne[tot]=h[x],h[x]=tot;} void dfs(int x,int las,int OvO) { fa[x]=OvO; for(RI i=h[x];i;i=ne[i]) { if(to[i]==las) continue; if(!c[x]) dfs(to[i],x,x); else dfs(to[i],x,OvO); } } int main() { int x,y; n=read(); bin[0]=1;for(RI i=1;i<=mxd;++i) bin[i]=bin[i-1]<<1; for(RI i=1;i<=n;++i) c[i]=read(); for(RI i=1;i<n;++i) x=read(),y=read(),add(x,y),add(y,x); dfs(1,0,0); tot=0;for(RI i=1;i<=n;++i) h[i]=0; for(RI i=1;i<=n;++i) if(!c[i]&&fa[i]) add(fa[i],i); for(RI i=1;i<=n;++i) if(!c[i]&&!fa[i]) work(i),ans^=sg[i]; if(!ans) puts("-1"); else { for(RI i=1;i<=n;++i) if(!c[i]&&!fa[i]) getans(rt[i],mxd,ans^sg[i]); sort(st+1,st+1+top); for(RI i=1;i<=top;++i) printf("%d\n",st[i]); } return 0; }