传送门
题解:
很显然这是一个ICG。由于删掉某个点到根的链之后可能会形成很多连通块,也就是独立局面,感觉用局面的NP性并不是很好做,考虑SG函数。
如果一个局面是终止局面,SG值为0。
否则,计算一个局面的SG值有两种方式:
- 一个游戏局面的SG值是该局面所有直接后继的SG值的mex
- 如果该游戏局面由若干个互不干涉的子局面构成,该局面的SG值是所有子局面SG值的异或和。
考虑怎么统计答案,我们需要计算删掉某一个点到根的链之后的局面的SG值,显然这个局面由若干连通块构成,考虑用第二种方式,也就是说我们需要计算以每个点为根的子树的SG值。
而以一个点为根的子树的SG值,我们考虑用第一种方式,那么我们需要考虑删掉子树中某个点到该点后的路径能够达到的SG值由哪些。
首先,如果它是白点,可以单独删除,那么需要考虑所有子树的xor和。
然后,考虑延伸到其中某一棵子树,显然其他子树的SG值直接xor进来得到该子树中能够达到的SG值。
于是用01Trie可以非常方便地支持打xortag,合并子树和查询mex了。
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const
namespace IO{
inline char get_char(){
static cs int Rlen=1<<22|1;
static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
template<typename T>
inline T get(){
char c;T num;
while(!isdigit(c=gc()));num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return num;
}
inline int gi(){return get<int>();}
}
using namespace IO;
using std::cerr;
using std::cout;
cs int N=1e5+7;
namespace Trie{
cs int N=::N*30,K=20;
int son[N][2],full[N],tot;
int tag[N],d[N];
inline void push_xor(int u,int v){if(!u)return ;
tag[u]^=v;if(v>>d[u]&1)std::swap(son[u][0],son[u][1]);
}
inline void pushdown(int u){
if(tag[u]){
push_xor(son[u][0],tag[u]);
push_xor(son[u][1],tag[u]);
tag[u]=0;
}
}
inline void ins(int &rt,int val,int de=K){
if(!rt)d[rt=++tot]=de;if(full[rt])return ;
pushdown(rt);if(!~de){full[rt]=true;return ;}
ins(son[rt][val>>de&1],val,de-1);
full[rt]=full[son[rt][0]]&&full[son[rt][1]];
}
inline int merge(int u,int v){
if(!u||!v)return u|v;
if(full[u])return u;
if(full[v])return v;
pushdown(u);pushdown(v);
son[u][0]=merge(son[u][0],son[v][0]);
son[u][1]=merge(son[u][1],son[v][1]);
full[u]=full[son[u][0]]&&full[son[u][1]];
return u;
}
inline int mex(int rt){
if(!rt)return 0;
int ans=0,cur=rt;
for(int re i=K;~i;--i){
if(!full[son[cur][0]])cur=son[cur][0];
else ans|=1<<i,cur=son[cur][1];
}
return ans;
}
}
int rt[N],col[N];
int el[N],nxt[N<<1],to[N<<1],ecnt;
inline void adde(int u,int v){
nxt[++ecnt]=el[u],el[u]=ecnt,to[ecnt]=v;
nxt[++ecnt]=el[v],el[v]=ecnt,to[ecnt]=u;
}
int nd[N],ct;
int sub[N];
void dfs1(int u,int p){
int son=0;
for(int re e=el[u],v=to[e];e;v=to[e=nxt[e]])
if(v!=p)dfs1(v,u),son^=sub[v];
if(!col[u])Trie::ins(rt[u],son);
for(int re e=el[u],v=to[e];e;v=to[e=nxt[e]])
if(v!=p){
Trie::push_xor(rt[v],son^sub[v]);
rt[u]=Trie::merge(rt[u],rt[v]);
}
sub[u]=Trie::mex(rt[u]);
}
void dfs2(int u,int p,int out){
int son=0;
for(int re e=el[u],v=to[e];e;v=to[e=nxt[e]])
if(v!=p)son^=sub[v];
if(!col[u]&&(out^son)==0)nd[++ct]=u;
for(int re e=el[u],v=to[e];e;v=to[e=nxt[e]])
if(v!=p)dfs2(v,u,out^son^sub[v]);
}
signed main(){
#ifdef zxyoi
freopen("cot.in","r",stdin);
#endif
int n=gi();
for(int re i=1;i<=n;++i)col[i]=gi();
for(int re i=1;i<n;++i)adde(gi(),gi());
dfs1(1,0);dfs2(1,0,0);
if(!ct)cout<<"-1\n";
else {
std::sort(nd+1,nd+ct+1);
for(int re i=1;i<=ct;++i)cout<<nd[i]<<"\n";
}
return 0;
}