1. 程式人生 > 其它 >CF1592C. Bakry and Partitioning

CF1592C. Bakry and Partitioning

原題連結:CF1592C. Bakry and Partitioning

題意:

給定一個\(n\)個點,\(n - 1\)條邊的樹,並且每個點都有權值\(w_i\),讓你最少割掉一條邊最多割掉\(k - 1\)條邊使得劃分後的子樹異或和相等。

思路:

  1. 首先根據異或的交換律結合律得知:如果所有點的權值\(\sum\nolimits_{i = 1}^n w_i = 0\),那麼我們隨意切一刀就行,所以這種情況下必然滿足題意。

  2. 再根據異或的一個性質應用,\(x \,\, \oplus \,\, x \,\, \oplus \,\, x \,\, \oplus \,\, = x\),考慮到,設\(\sum\nolimits_{i = 1}^n w_i = x\)

    ,那麼我們只需要判斷這個樹是否可以分成三份異或和都可以等於\(0\)的子樹即可,如果可以,那麼還有檢查\(k\)是否大於\(2\)

\(f[u]\)表示以\(u\)為根節點的子樹的所有結點的異或和,那麼\(DFS\)跑樹形\(DP\)即可。

// Problem: C. Bakry and Partitioning
// Contest: Codeforces - Codeforces Round #746 (Div. 2)
// URL: https://codeforces.com/contest/1592/problem/C
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>

using namespace std;

const int N = 1E5 + 10, M = N * 2;
int h[N], e[M], ne[M], idx;
int w[N], f[N], cnt = 0;
int n, k;
int xorsum = 0;

void add(int a, int b) {
	e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

int dfs(int u, int fa) {
	f[u] = w[u];
	for (int i = h[u]; i != -1; i = ne[i]) {
		int j = e[i];
		if (j == fa) continue;
		int t = dfs(j, u);
		f[u] ^= t;
	}
	
	if (f[u] == xorsum) cnt++, f[u] = 0;
	
	return f[u];
}

int main() {
	int t;
	scanf("%d", &t);
	while (t--) {
		xorsum = 0, memset(h, -1, sizeof h), idx = 0, cnt = 0;

		scanf("%d%d", &n, &k);
		
		for (int i = 1; i <= n; i++) {
			cin >> w[i];
			xorsum ^= w[i];
			f[i] = 0;
		}
		//case 1:如果陣列異或和為0,那麼隨意切一刀
		//case 2:x xor x xor x = x設總和為x,那麼分成三份每一部分都是x
		//令f[u]為以u為根的子樹的異或和
		
		for (int i = 0; i < n - 1; i++) {
			int u, v; scanf("%d%d", &u, &v);
			add(u, v), add(v, u);
		}
		
		if (xorsum == 0) puts("YES");
		else {
			dfs(1, -1);
			if (cnt >= 3 && k > 2) puts("YES");
			else puts("NO");
		}
	}
    return 0;
}