https://codeforces.com/contest/1592/problem/C
题目大意:给出一个有 n n n 个节点的点权树和一个限制 k k k ,现在问能否删除至少一条,至多 k − 1 k-1 k−1 条边,使得剩下的连通块里的点权异或和相等。
首先,如果拆分成偶数个连通块,那么我们可以将这偶数个连通块一分为二,合并成异或和相等的两个连通块,那么对于这种情况而言,其整棵树的异或和肯定为 0 0 0 ,而且我们发现如果整颗树异或和为 0 0 0 ,那么其随便切一刀都是相等的两个块。所以如果异或和为 0 0 0 就肯定行。那么主要是讨论奇数情况,那么与偶数类似,如果是奇数块,我们最终可以合并成 3 3 3 块,且显然每一小块的异或和肯定是等于整棵树的异或和的。那么实际上我们只要找到两块不相交的异或和为整树异或和的连通块。如果这样的两个连通块都是子树那就好些了,但是显然不一定是,切成三块必至少有一块为非子树。我们发现,对于非子树的连通块而言,这个连通块下面肯定有颗子树要被切出来,那么我们在深搜的时候就将已经确定为可以切割的子树抹去,实际上也就是另其子树异或值为 0 0 0 ,这样如果上面有原本是非子树的连通块也变成一颗子树了,就可以用一样的方式去计算了,最终只要找到了两个及以上的这样的连通块且 k ≥ 3 k≥3 k≥3 就表明可以切割。
#includeusing namespace std; const int N = 1e5 + 10; int n, k, zx[N], a[N], all, cnt; vector g[N]; void dfs(int u, int fa) { zx[u] = a[u]; for (auto v : g[u]) { if (v ^ fa) { dfs(v, u); zx[u] ^= zx[v]; } } if (zx[u] == all) { cnt++; zx[u] = 0; } } int main() { #ifndef ONLINE_JUDGE freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout); #endif int T; scanf("%d", &T); while(T--) { all = cnt = 0; scanf("%d%d", &n, &k); for (int i = 1; i <= n; ++i) { scanf("%d", &a[i]); all ^= a[i]; g[i].clear(); } for (int i = 0; i < n - 1; ++i) { int u, v; scanf("%d%d", &u, &v); g[u].push_back(v); g[v].push_back(u); } dfs(1, 0); if (all == 0) { puts("YES"); } else if (k - 1 >= 2 && cnt >= 2) { puts("YES"); } else { puts("NO"); } } }



