CF1779F

· · 题解

翻译是我给的,但题解是翻译通过之后几天才写的,原因是口胡完了不想动代码。

题目大意

给定一棵 n(n\le 2\times 10^5) 个节点的树,第 i 个节点有非负权值 a_i\le 31。定义一次操作为:

已知这一操作最多可以执行 2n 次。Mars 想知道是否可以通过执行若干次这一操作使得树上所有节点权值均为 0

如果可以,输出第一行一个非负整数 q 表示操作次数,第二行为 q 个正整数,表示每次操作选择的节点编号 i;如果不可以,输出一行一个整数 -1

你不需要最小化 q,输出任意一组解即可。

大体思路

sz_u 表示 u 为根的子树大小,son_u 表示 u 的儿子个数。

这题口胡之所以容易,是因为很快能够发现两个结论。

因此,对奇数大小的子树的根操作是无意义的,对偶数大小的子树的根节点最多操作两次(而且只有最后全部清零时才会操作两次),因此操作次数不超过 2n 是必然的。

然后,我们可以使用树形 DP 求解这一问题。这很明显是一个背包。

F(u,x,i) 表示考虑 u 的前 i 个儿子节点,能否是的以 u 为根的子树的点权异或和为 x。转移时枚举儿子节点 v_i 以及异或和即可,时间复杂度 O(n\omega^2),其中 \omega\le 31 表示值域。

至于输出方案,把原先 DP 的 01 状态改为记录转移来的异或和即可,初始全部设为 -1,对于 sz_u 为偶数的情况,令 F(u,0,son_u)=-2 做特殊标记。

由于 \sum son_uO(n) 的,直接开 DP 数组会 MLE,采用 $\texttt{std::vector}

## 参考代码 ```cpp #include <bits/stdc++.h> using namespace std; #define rep(ii,aa,bb) for(re int ii = aa; ii <= bb; ii++) #define Rep(ii,aa,bb) for(re int ii = aa; ii >= bb; ii--) typedef long long ll; typedef unsigned long long ull; typedef double db; typedef pair<int, int> PII; const int maxn = 2e5 + 5; namespace IO_ReadWrite { #define re register #define gg (p1 == p2 && (p2 = (p1 = _buf) + fread(_buf, 1, 1<<21, stdin), p1 == p2) ? EOF :*p1++) char _buf[1<<21], *p1 = _buf, *p2 = _buf; template <typename T> inline void read(T &x){ x = 0; re T f=1; re char c = gg; while(c > 57 || c < 48){if(c == '-') f = -1;c = gg;} while(c >= 48 &&c <= 57){x = (x<<1) + (x<<3) + (c^48);c = gg;} x *= f;return; } inline void ReadChar(char &c){ c = gg; while(!isalpha(c)) c = gg; } template <typename T> inline void write(T x){ if(x < 0) putchar('-'), x = -x; if(x > 9) write(x/10); putchar('0' + x % 10); } template <typename T> inline void writeln(T x){write(x); putchar('\n');} } using namespace IO_ReadWrite; int n, val[maxn], sz[maxn], son[maxn]; vector <int> e[maxn]; vector <int> f[maxn][35]; // 一开始脑子糊涂了,其实值域<=31 inline void dfs(int u, int fa) { sz[u] = 1; rep(j, 0, 32) rep(t, 0, son[u]) f[u][j][t] = -1; f[u][val[u]][0] = 0; rep(i, 1, son[u]) { int v = e[u][i - 1]; dfs(v, u); sz[u] += sz[v]; rep(j, 0, 32) if(f[u][j][i - 1] != -1) rep(k, 0, 32) if(f[v][k][son[v]] != -1) f[u][j ^ k][i] = k; } if(!(sz[u] & 1)) f[u][0][son[u]] = -2; } vector <int> ans; inline void output(int u, int x) { if(f[u][x][son[u]] == -2) { ans.push_back(u); return; } Rep(i, son[u], 1) { int v = e[u][i - 1]; output(v, f[u][x][i]); x ^= f[u][x][i]; } } int main () { read(n); rep(i, 1, n) read(val[i]); rep(i, 2, n) { int p; read(p); e[p].push_back(i); son[p] ++; } // rep(i, 1, n) printf("%d %d\n", son[i], e[i].size()); rep(i, 1, n) rep(j, 0, 32) f[i][j].resize(son[i] + 1); dfs(1, 0); if(f[1][0][son[1]] == -1) return writeln(-1), 0; output(1, 0); ans.push_back(1); writeln(ans.size()); for(auto x : ans) write(x), putchar(' '); return 0; } ``` ## 后记 过年前开题的,回老家一路上口胡的,大年三十写完的。 希望今年春晚好看,祝大家新年快乐!