Description
分曹射覆蜡灯红,膜拜神犇lzr;
渚清沙白鸟飞回,长跪巨神ljw。
lzr就是被称为hack狂魔的qmqmqm,相比很多人都已经知道了。
ljw虽然没有lzr有名,但是在cf、bc等比赛里的hack次数也是数一数二的。
SD的这两位神犇今天决定进行一场hack比赛。
经过研究,他们决定hack SD的另一位神犇jzh做过的题。
我们设jzh已经做过了n道题。这些题目因为知识点之间的联系而形成了一棵树结构。1号题目(A+B problem)是这棵树的根。
因为slyz也不乏hack高手,所以jzh做的这n道题有些已经被hack了。
现在ljw先手,两人轮流操作。一次操作为:选择一道没有被hack过的题x,然后将x到1的路径上的所有没有被hack过的题全部hack掉。
无法操作者输。
我们假设ljw和lzr的智商都是无比的高(事实也是如此),都会采取对自己最有利的操作。
ljw当然想赢下这场比赛了!于是他想算一下最终他能否赢下比赛。如果他能赢,他还想知道在保证他赢的情况下第一步他选择哪些题。
这么简单的问题ljw神犇当然会了。不过他想考考你。
Input
第一行一个整数n(n<=100000),代表jzh神犇做过的题数。
第二行n个整数,每个整数为0或1。其中第i个数为0代表第i道题还没有被hack,第i个数为1代表第i道题已经被slyz的神犇hack了。
接下来n-1行描述jzh做过的题目形成的树。每行两个整数u,v代表第u道题和第v道题之间有一条边。
Output
如果ljw不能赢得比赛,那么输出-1。
否则升序输出所有题号x。x满足ljw在保证赢的情况下第一步可以选择x。
Sample Input
8
1 1 0 1 0 0 1 0
1 2
1 3
2 6
3 4
3 5
5 7
7 8
Sample Output
5
HINT
数据范围:1<=u,v<=n<=100000
分析:
昨天选讲是提到了这一题。
这种题肯定想到
sg
s
g
函数。
因为一开始有的点已经染色,所以我们新建一个森林,每个点的父亲是该点到根路径上的第一个白点(黑点没影响),正确性显然。
我们设
sg(i)
s
g
(
i
)
为子树
i
i
的值;
g(x)
g
(
x
)
为删除根到
x
x
的路径后,剩余部分函数的异或值。
那么,
sg(x)=mex(g(y),y∈subtree(x))
s
g
(
x
)
=
m
e
x
(
g
(
y
)
,
y
∈
s
u
b
t
r
e
e
(
x
)
)
。
考虑递推求
sg
s
g
值,我们开一个东西把子树
y
y
的值记录下来,然后当我们枚举到父亲
x
x
时,显然子树的每个
g
g
值都异或上了的其他儿子的
sg
s
g
值。对于
x
x
其他儿子也同理。当然还要加上当前点的值,这个显然是所有子树的
sg
s
g
值的异或和。然后我们可以对当前点求
mex
m
e
x
,显然要维护一个数据结构来实现给全部数异或一个数,插入一个数和求
mex
m
e
x
,显然一个字典树就可以了,打上一个
lazy
l
a
z
y
和记录一个
full
f
u
l
l
表示该儿子是否为满二叉树。然后
trie
t
r
i
e
合并直接向线段树合并一样就可以了。
代码:
/**************************************************************
Problem: 4134
User: liangzihao
Language: C++
Result: Accepted
Time:3900 ms
Memory:92900 kb
****************************************************************/
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <vector>
const int maxd=30;
const int maxn=1e5+7;
using namespace std;
int op[maxn],sg[maxn],ls[maxn],root[maxn],fa[maxn],bit[35];
int n,x,y,cnt;
int ans[maxn];
struct node{
int l,r;
int full,lazy;
vector <int> a;
}t[maxn*maxd];
struct edge{
int y,next;
}g[maxn*2];
void add(int x,int y)
{
g[++cnt]=(edge){y,ls[x]};
ls[x]=cnt;
}
void dfs(int x,int f,int top)
{
fa[x]=top;
for (int i=ls[x];i>0;i=g[i].next)
{
int y=g[i].y;
if (f==y) continue;
if (op[x]) dfs(y,x,top);
else dfs(y,x,x);
}
}
void clean(int p,int now)
{
if (now<0) return;
if (t[p].lazy)
{
if (t[p].l) t[t[p].l].lazy^=t[p].lazy;
if (t[p].r) t[t[p].r].lazy^=t[p].lazy;
if (t[p].lazy&bit[now]) swap(t[p].l,t[p].r);
t[p].lazy=0;
}
}
void merge(int &p,int q,int now)
{
clean(p,now); clean(q,now);
if (!p||!q)
{
p=p+q;
return;
}
if (now<0)
{
t[p].full=t[p].full|t[q].full;
for (int i=0;i<t[q].a.size();i++) t[p].a.push_back(t[q].a[i]);
t[q].a.clear();
return;
}
merge(t[p].l,t[q].l,now-1);
merge(t[p].r,t[q].r,now-1);
t[p].full=t[t[p].l].full&t[t[p].r].full;
}
void ins(int &p,int now,int x,int num)
{
if (!p) p=++cnt;
clean(p,now);
if (now<0)
{
t[p].a.push_back(num);
t[p].full=1;
return;
}
if (x&bit[now]) ins(t[p].r,now-1,x,num);
else ins(t[p].l,now-1,x,num);
t[p].full=t[t[p].l].full&t[t[p].r].full;
}
int mex(int p,int now)
{
clean(p,now);
if (now<0) return 0;
if (t[t[p].l].full) return bit[now]+mex(t[p].r,now-1);
else return mex(t[p].l,now-1);
}
void solve(int x,int f)
{
int w=0;
for (int i=ls[x];i>0;i=g[i].next)
{
int y=g[i].y;
if (f==y) continue;
solve(y,x);
w^=sg[y];
}
for (int i=ls[x];i>0;i=g[i].next)
{
int y=g[i].y;
if (f==y) continue;
int l=w^sg[y];
t[root[y]].lazy^=l;
merge(root[x],root[y],maxd);
}
ins(root[x],maxd,w,x);
sg[x]=mex(root[x],maxd);
}
void find(int p,int now,int x)
{
clean(p,now);
if (now<0)
{
for (int i=0;i<t[p].a.size();i++) ans[++cnt]=t[p].a[i];
return;
}
if (x&bit[now]) find(t[p].r,now-1,x);
else find(t[p].l,now-1,x);
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&op[i]);
for (int i=1;i<=n-1;i++)
{
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs(1,0,0);
memset(ls,0,sizeof(ls));
cnt=0;
for (int i=1;i<=n;i++)
{
if ((fa[i]) && (!op[i])) add(fa[i],i);
}
bit[0]=1;
for (int i=1;i<=maxd;i++) bit[i]=bit[i-1]*2;
int lim=0;
cnt=0;
for (int i=1;i<=n;i++)
{
if ((!fa[i])&&(!op[i]))
{
solve(i,0);
lim^=sg[i];
}
}
if (!lim) printf("-1");
else
{
cnt=0;
for (int i=1;i<=n;i++)
{
if ((!fa[i])&&(!op[i]))
{
find(root[i],maxd,lim^sg[i]);
}
}
sort(ans+1,ans+cnt+1);
for (int i=1;i<=cnt;i++) printf("%d\n",ans[i]);
}
}