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[x]为以x为子树的sg值,rem[x]为删掉从x到根的路径后剩余的游戏的sg值的异或和,x的rem是他所有儿子的sg值的异或和,而对于x的每一个儿子y,y的子树里的所有rem值都会异或rem[x]^sg[y] 然后我们求第一个不出现在子树内的rem集合里的非负整数即是x的sg值对每个点建立一个权值线段树,维护子树内所有的rem值是否出现过,然后在线段树上爬即可查出这个点的sg值。对于一个点,如果没有预先被hack,那么把他的rem插入这个点的线段树中,然后把这棵线段树和他所有儿子的线段树进行线段树合并。由于要把某个子树里的所有值都异或一个数,那么可以把权值线段树的区间设为0~2的幂-1,然后在线段树上打异或标记,如果对应的位是1的话就交换左右儿子即可 最后再dfs一遍树处理每个点的rem判断是否为0即可输出答案
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <set>
#include <queue>
#include <algorithm>
#include <vector>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <stack>
#define INF 2147483647
#define LL long long
#define clr(x) memset(x, 0, sizeof x)
#define digit (ch < '0' || ch > '9')
using namespace std;
template <class T> inline void read(T &x) {
int flag = 1; x = 0;
register char ch = getchar();
while( digit) { if(ch == '-') flag = -1; ch = getchar(); }
while(!digit) { x = (x<<1)+(x<<3)+ch-'0'; ch = getchar(); }
x *= flag;
}
const int maxn = 100005;
const int maxm = 4000005;
int n,u,v,tot,cnt,N,D;
int a[maxn],t[maxn],sg[maxn],rem[maxn],bl[maxn];
int son[maxm][2],rt[maxm],siz[maxm],rev[maxm];
struct edge { int to,frm; } e[maxn<<1];
inline void add(int x, int y) { e[++cnt].to = y; e[cnt].frm = t[x]; t[x] = cnt; }
inline void bv(int x, int y, int d) {
rev[x] ^= y;
if(rev[x]&(1<<d)) swap(son[x][0], son[x][1]);
}
inline void ud(int x) { siz[x] = siz[son[x][0]]+siz[son[x][1]]; }
inline void pd(int x, int d) {
if(rev[x]) {
bv(son[x][0], rev[x], d-1);
bv(son[x][1], rev[x], d-1);
rev[x] = 0;
}
}
void ins(int &x, int y, int z, int p, int d) {
if(!x) {
x = ++tot;
son[x][0] = son[x][1] = siz[x] = rev[x] = 0;
}
if(y == z) { siz[x] = 1; return ; }
pd(x, d);
int mid = y+z>>1;
if(p <= mid) ins(son[x][0], y, mid, p, d-1);
else ins(son[x][1], mid+1, z, p, d-1);
}
int merge(int x, int y, int l, int r, int d) {
if(!x || !y) return x+y;
if(l == r) { siz[x] |= siz[y]; return x; }
int mid = l+r>>1;
pd(x, d); pd(y, d);
son[x][0] = merge(son[x][0], son[y][0], l, mid, d-1);
son[x][1] = merge(son[x][1], son[y][1], mid+1, r, d-1);
ud(x);
return x;
}
int ask(int x, int y, int z, int d) {
if(y == z) return y;
pd(x, d);
int mid = y+z>>1;
if(siz[son[x][0]] == mid-y+1) return ask(son[x][1], mid+1, z, d-1);
else return ask(son[x][0], y, mid, d-1);
}
void dfs(int x, int f) {
rem[x] = 0;
for(int y, i = t[x]; i; i = e[i].frm) {
y = e[i].to;
if(y != f) dfs(y, x), rem[x] ^= sg[y];
}
if(!a[x]) ins(rt[x], 0, N, rem[x], D);
for(int y, i = t[x]; i; i = e[i].frm) {
y = e[i].to;
if(y != f) bv(rt[y], rem[x]^sg[y], D), rt[x] = merge(rt[x], rt[y], 0, N, D);
}
sg[x] = ask(rt[x], 0, N, D);
}
inline void find(int x, int f) {
if(!a[x] && !rem[x]) bl[x] = 1;
for(int y, i = t[x]; i; i = e[i].frm) {
y = e[i].to;
if(y != f) rem[y] ^= rem[x]^sg[y], find(y, x);
}
}
int main () {
read(n);
for(register int i = 1; i <= n; i++) read(a[i]);
for(register int i = 1; i < n; i++) read(u), read(v), add(u, v), add(v, u);
for(N = 1; N <= n; N <<= 1) D++;
N--; D--;
dfs(1, 0);
if(!sg[1]) { printf("-1\n"); return 0; }
find(1, 0);
for(int i = 1; i <= n; i++) if(bl[i]) printf("%d\n",i);
return 0;
}