Problem:
Score : 1000 points

Problem Statement

You are given a tree with N vertices. The vertices are numbered 0 through N-1, and the edges are numbered 1 through N-1.
Edge i connects Vertex x_i and y_i, and has a value a_i.
You can perform the following operation any number of times:

Choose a simple path and a non-negative integer x, then for each edge e that belongs to the path, change a_e by executing a_e ← a_e ⊕ x (⊕ denotes XOR).

Your objective is to have a_e = 0 for all edges e.
Find the minimum number of operations required to achieve it.

Constraints

2 ≤ N ≤ 10^5

0 ≤ x_i,y_i ≤ N-1

0 ≤ a_i ≤ 15

The given graph is a tree.

All input values are integers.

Input

Input is given from Standard Input in the following format:

N
x_1 y_1 a_1
x_2 y_2 a_2
:
x_{N-1} y_{N-1} a_{N-1}

Output

Find the minimum number of operations required to achieve the objective.

Sample Input 1

5
0 1 1
0 2 3
0 3 6
3 4 4

Sample Output 1

3

The objective can be achieved in three operations, as follows:

First, choose the path connecting Vertex 1, 2, and x = 1.

Then, choose the path connecting Vertex 2, 3, and x = 2.

Lastly, choose the path connecting Vertex 0, 4, and x = 4.

Sample Input 2

2
1 0 0

Sample Output 2

0

Solution in Java8 (OpenJDK 1.8.0) (judge verdict: Runtime Error)

import java.io.*;
import java.util.*;

public class Main {
	
	int[][] g;

	void submit() {
		int n = nextInt();
		g = new int[n][];
		int[] a = new int[3 * n - 3];
		int[] deg = new int[n];
		for (int i = 0; i < n - 1; i++) {
			a[3 * i] = nextInt();
			a[3 * i + 1] = nextInt();
			a[3 * i + 2] = nextInt();
			deg[a[3 * i]]++;
			deg[a[3 * i + 1]]++;
		}

		for (int i = 0; i < n; i++) {
			g[i] = new int[2 * deg[i]];
		}

		for (int i = 0; i < 3 * n - 3; i += 3) {
			int v = a[i];
			int u = a[i + 1];
			int lbl = a[i + 2];
			--deg[v];
			g[v][2 * deg[v]] = u;
			g[v][2 * deg[v] + 1] = lbl;
			--deg[u];
			g[u][2 * deg[u]] = v;
			g[u][2 * deg[u] + 1] = lbl;
		}
		
		dfs(3, -1);
		
		int groups = 0;
		groups += cnt[0];
		cnt[0] = 0;
		for (int i = 1; i < 16; i++) {
			groups += cnt[i] / 2;
			cnt[i] %= 2;
		}
		
		groups += go(cnt);
		out.println(n - 1 - groups);
	}
	
	int go(int[] arr) {
		int[] xor = new int[1 << 15];
		for (int i = 0; i < 15; i++) {
			xor[1 << i] = i + 1;
		}
		for (int i = 1; i < xor.length; i++) {
			int low = i & -i;
			xor[i] = xor[low] ^ xor[i ^ low];
		}
		
		int[] dp = new int[1 << 15];
		dp[0] = 0;
		for (int mask = 1; mask < 1 << 15; mask++) {
			for (int sub = mask; sub > 0; sub = (sub - 1) & mask) {
				dp[mask] = Math.max(dp[mask], dp[mask ^ sub] + (xor[sub] == 0 ? 1 : 0));
			}
		}
		
		int look = 0;
		for (int i = 0; i < 15; i++) {
			if (cnt[i + 1] > 0) {
				look |= 1 << i;
			}
		}
		return dp[look];
	}
	
	int[] cnt = new int[16];
	
	int dfs(int v, int p) {
		int up = 0;
		int allChild = 0;
		for (int i = 0; i < g[v].length; i += 2) {
			int u = g[v][i];
			int x = g[v][i + 1];
			if (u == p) {
				up = x;
			} else {
				allChild ^= dfs(u, v);
			}
		}
		if (p != -1) {
//			System.err.println(ret);
			cnt[up ^ allChild]++;
		}
		return up;
	}

	void preCalc() {

	}

	void stress() {

	}

	void test() {

	}

	Main() throws IOException {
		br = new BufferedReader(new InputStreamReader(System.in));
		out = new PrintWriter(System.out);
		preCalc();
		submit();
		//stress();
		//test();
		out.close();
	}

	static final Random rng = new Random();

	static int rand(int l, int r) {
		return l + rng.nextInt(r - l + 1);
	}

	public static void main(String[] args) throws IOException {
		new Main();
	}

	BufferedReader br;
	PrintWriter out;
	StringTokenizer st;

	String nextToken() {
		while (st == null || !st.hasMoreTokens()) {
			try {
				st = new StringTokenizer(br.readLine());
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
		return st.nextToken();
	}

	String nextString() {
		try {
			return br.readLine();
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	int nextInt() {
		return Integer.parseInt(nextToken());
	}

	long nextLong() {
		return Long.parseLong(nextToken());
	}

	double nextDouble() {
		return Double.parseDouble(nextToken());
	}
}