每次操作实际上相当于把点到根上所有点消除。$O({n^2})$暴力显然,
用trie维护每棵子树的后继局面,然后需要进行trie树异或操作和tire树合并操作。
异或操作直接打标记
合并操作前,类似启发式合并,先把子树较小的的标记全部下放,然后全部按照另一棵的标记转好,然后把它合并到另一颗里面去。正确性因为有标记的地方都转过,再转一次就是原来的了。
复杂度$O(nlog^2n)$
#include <iostream> #include <cstdio> #include <cmath> #include <cstring> #include <algorithm> using namespace std; #define rep(i, l, r) for (int i = l; i <= r; i++) #define drep(i, r, l) for (int i = r; i >= l; i--) typedef long long ll; const int N = 100008, D = 20; int n, TOT, head[N], tot, fa[N], sg[N], a[N], rt[N], ans[N]; bool rev[N][D + 5]; struct Edge{int next, node;}e[N << 1]; int xgw; inline void add(int x, int y) { e[++TOT].next = head[x], head[x] = TOT, e[TOT].node = y; e[++TOT].next = head[y], head[y] = TOT, e[TOT].node = x; } struct Node { int s[2], sum, sz; }t[N * (D + 5)]; inline void updata(int x) { t[x].sum = t[t[x].s[0]].sum + t[t[x].s[1]].sum; t[x].sz = 1 + (t[x].s[0] ? 1 : 0) + (t[x].s[1] ? 1 : 0); } void Insert(int u, int x, int d, int num) { if (!d) {t[x].sum = 1; return;} int p = (num & (1 << d - 1)) ? 1 : 0; p ^= rev[u][d]; if (!t[x].s[p]) t[x].s[p] = ++tot; Insert(u, t[x].s[p], d - 1, num); updata(x); } void calc(int u, int d, int num) { if (!rt[u]) return; rep(i, 1, d) rev[u][i] ^= (num & (1 << i - 1)) ? 1 : 0; } int Find(int u, int x, int d, int cur) { if (!x || !d) return cur; int ls = t[x].s[0 ^ rev[u][d]], rs = t[x].s[1 ^ rev[u][d]]; //if (xgw) printf("->%d %d %d %d ", u, d, t[ls].sum, t[rs].sum); if (t[ls].sum != (1 << d - 1)) return Find(u, ls, d - 1, cur); return Find(u, rs, d - 1, cur + (1 << d - 1)); } void Turn(int u, int x, int d) { if (!d || !x) return; if (rev[u][d]) swap(t[x].s[0], t[x].s[1]); Turn(u, t[x].s[0], d - 1); Turn(u, t[x].s[1], d - 1); } int Merge(int u, int x, int v, int y, int d) { if (!d) return x; if (!x || !y) return x + y; rep(i, 0, 1) { int j = i ^ rev[u][d], k = i ^ rev[v][d]; t[x].s[j] = Merge(u, t[x].s[j], v, t[y].s[k], d - 1); } updata(x); return x; } int MERGE(int u, int v) { if (t[rt[u]].sz > t[rt[v]].sz) swap(u, v); Turn(u, rt[u], D); memcpy(rev[u], rev[v], sizeof(rev[v])); Turn(u, rt[u], D); return Merge(v, rt[v], u, rt[u], D); } void write(int u, int x, int d, int cur) { if (!x) return; if (!d) {printf("%d ", cur); return;} int ls = t[x].s[0 ^ rev[u][d]], rs = t[x].s[1 ^ rev[u][d]]; write(u, ls, d - 1, cur); write(u, rs, d - 1, cur + (1 << d - 1)); } void dfs(int u) { int Leaf = 1; for (int i = head[u], v; v = e[i].node, i; i = e[i].next) if (v != fa[u]) Leaf = 0, fa[v] = u, dfs(v); if (Leaf) { sg[u] = !a[u]; if (sg[u]) Insert(u, rt[u] = ++tot, D, 0); return; } int x = 0; for (int i = head[u], v; v = e[i].node, i; i = e[i].next) if (v != fa[u]) { calc(u, D, sg[v]); calc(v, D, x); rt[u] = MERGE(u, v); x ^= sg[v]; } if (!a[u]) Insert(u, rt[u] ? rt[u] : rt[u] = ++tot, D, x); sg[u] = Find(u, rt[u], D, 0); } void Tour(int u, int cur) { cur ^= sg[u]; for (int i = head[u], v; v = e[i].node, i; i = e[i].next) if (v != fa[u]) cur ^= sg[v]; if (!a[u] && !cur) ans[++ans[0]] = u; for (int i = head[u], v; v = e[i].node, i; i = e[i].next) if (v != fa[u]) Tour(v, cur); } void solve() { dfs(1); //rep(i, 1, n) printf("[%d %d] ", i, sg[i]); if (!sg[1]) printf("-1 "); else { Tour(1, sg[1]); sort(ans + 1, ans + ans[0] + 1); rep(i, 1, ans[0]) printf("%d ", ans[i]); } } int main() { #ifndef ONLINE_JUDGE freopen("input.txt","r",stdin); freopen("output.txt","w",stdout); #endif scanf("%d", &n); rep(i, 1, n) scanf("%d", &a[i]); rep(i, 1, n - 1) { int u, v; scanf("%d%d", &u, &v); add(u, v); } solve(); #ifndef ONLINE_JUDGE fclose(stdin); fclose(stdout); #endif return 0; }