Problem:
C: サボテンクエリ
問題文
単純無向グラフで、任意の辺が高々 $1$ つの単純閉路にしか含まれないようなものをサボテングラフと呼ぶことにします。
$N$ 頂点 $M$ 辺の連結なサボテングラフ $G$ が与えられます。
各頂点は $1$ から $N$ まで番号が付いています。
また、$i$ 個目の辺は頂点 $a_i$ と頂点 $b_i$ を結んでおり、コストは $c_i$ です。
グラフ $G$ 上の単純パスのコストを、そのパス上に含まれる全ての辺のコストの XOR と定めます。
以下のような形式の $Q$ 個のクエリに答えてください。
x_i y_i k_i
― 頂点 $x_i$ と頂点 $y_i$ を繋ぐすべての単純パスのコストを列挙して重複する値を除き、小さい順に並べた列を $d = d_1, d_2, ... , d_L$ としたときに、$d_{k_i}$ を求めよ。ただし、このコスト列 $d$ の長さ $L$ が $k_i$ より小さい場合は $-1$ とする。
制約
$2 \leq N \leq 10^5$
$N - 1 \leq M \leq 2 \times 10^5$
$1 \leq a_i, b_i \leq N$
$a_i \neq b_i$
$0 \leq c_i < 2^{30}$
$1 \leq Q \leq 2 \times 10^5$
$1 \leq x_i, y_i \leq N$
$x_i \neq y_i$
$1 \leq k_i \leq 2^{30}$
与えられるグラフは連結なサボテングラフである。
入力は全て整数である。
入力
入力は以下の形式で標準入力から与えられる。
$N$ $M$
$a_1$ $b_1$ $c_1$
$a_2$ $b_2$ $c_2$
$:$
$a_M$ $b_M$ $c_M$
$Q$
$x_1$ $y_1$ $k_1$
$x_2$ $y_2$ $k_2$
$:$
$x_Q$ $y_Q$ $k_Q$
出力
$Q$ 個のクエリの答えを一行ごとに順番に出力せよ。
入力例 1
4 4
1 2 1
1 3 8
3 2 0
1 4 7
4
1 2 1
2 1 2
1 4 1
3 4 1073741824
出力例 1
1
8
7
-1
頂点 $1$ から頂点 $2$ への単純パスは、辺 $1$ のみを経由するものと、辺 $2, 3$ を経由するもので、コストはそれぞれ $1$, $8$ です。なので、$1$ つめのクエリの答えは $1$となります。
頂点 $2$ から頂点 $1$ への単純パスも上記と同様なので、 $2$ つ目のクエリの答えは $8$ となります。
頂点 $1$ から頂点 $4$ への単純パスは、辺 $4$ のみを経由するもののみで、コストは $7$ です。なので、$3$ つ目のクエリの答えは $7$ となります。
頂点 $3$ から頂点 $4$ への単純パスは、辺 $2$, $4$ を経由するものと、辺 $3$, $1$, $4$ を経由するもので、コストはそれぞれ $15$, $6$ です。$1073741824$ 番目に小さいコストは存在しないので、$4$ つ目のクエリの答えは $-1$ となります。
入力例 2
13 15
1 2 1
2 3 2
3 4 3
4 5 1
5 1 2
5 6 4
6 7 15
7 8 9
8 6 7
2 9 5
9 10 5
10 2 2
3 11 3
11 12 2
11 13 1
8
12 13 1
1 11 2
9 5 4
2 7 3
6 12 2
9 7 5
10 3 3
3 12 2
出力例 2
3
3
7
10
7
-1
2
-1

Submitted solutions:

code_snippet: #include <vector>
#include <algorithm>
#include <cmath>
#include <numeric>
#include <iostream>
#include <iomanip>
#include <unordered_map>
#include <climits>
#include <queue>
#include <stack>
#include <random>
#include <set>
#include <cassert>
#include <iterator>
#include <type_traits>
#include <map>
#include <array>
#include <sstream>
#include <bitset>
#include <fstream>

constexpr int BIT_COUNT = 30;
bool add(std::array<int, BIT_COUNT>& arr, const int value) {
	int v{ value };
	for (auto i = 0; i < BIT_COUNT && v != 0; ++i) {
		if ((v >> (BIT_COUNT - i - 1)) & 1) {
			if (arr[i]) {
				v ^= arr[i];
			}
			else {
				arr[i] = v;
				return true;
			}
		}
	}
	return false;
};
class f2_vec {
	struct Pair {
		int first, second;
		Pair operator^(const Pair other) const {
			return { first ^ other.first, second ^ other.second };
		}
		Pair& operator^=(const Pair other) {
			first ^= other.first;
			second ^= other.second;
			return *this;
		}
	};
public:
	std::array<Pair, BIT_COUNT> vec;
	std::vector<std::pair<int, int>> used;
	void add(const int depth, const int value) {
		if (value == 0) return;
		Pair pair{ 0, value };
		for (auto i = 0; i < BIT_COUNT; ++i) {
			if ((pair.second >> (BIT_COUNT - i - 1)) & 1) {
				if (vec[i].second) {
					pair ^= vec[i];
				}
				else {
					vec[i].first = (1 << used.size()) | pair.first;
					vec[i].second = pair.second;
					used.emplace_back(depth, value);
					return;
				}
			}
		}
		int min_vec{ -1 };
		for (auto i = 0; i < used.size(); ++i) {
			if ((pair.first >> i) & 1) {
				if (min_vec == -1 || used[min_vec].first > used[i].first) {
					min_vec = i;
				}
			}
		}
		used[min_vec] = std::make_pair(depth, value);
		const auto mask = pair.first ^ (1 << min_vec);
		for (auto i = 0; i < BIT_COUNT; ++i) {
			if ((vec[i].first >> min_vec) & 1) {
				vec[i].first ^= mask;
			}
		}
	}
	std::vector<std::pair<int, int>> filter(const int depth) const {
		std::vector<std::pair<int, int>> result;
		std::copy_if(used.begin(), used.end(), std::back_inserter(result), [depth](const auto pair) {return pair.first > depth; });
		return result;
	}
};

void solve(const int n, const int m, const std::vector<std::tuple<int, int, int>> edges, const int q, const std::vector<std::tuple<int, int, int>> queries) {
	std::vector<std::vector<std::pair<int, int>>> graph(n);
	for (const auto [x, y, c] : edges) {
		graph[x].emplace_back(y, c);
		graph[y].emplace_back(x, c);
	}
	std::vector<int> queue(n), depth(n, -1), xor_from_root(n);
	std::vector<std::vector<int>> doubling(n);
	{
		int begin{ 0 }, end{ 0 };
		queue[end++] = 0;
		depth[0] = 0;
		while (begin < end) {
			const auto current = queue[begin++];
			for (const auto [next, c] : graph[current]) {
				if (depth[next] != -1) continue;
				depth[next] = depth[current] + 1;
				xor_from_root[next] = c ^ xor_from_root[current];
				queue[end++] = next;
				auto& ancestor = doubling[next];
				ancestor.push_back(current);
				while (ancestor.size() <= doubling[ancestor.back()].size()) {
					ancestor.push_back(doubling[ancestor.back()][ancestor.size() - 1]);
				}
			}
		}
	}
	std::vector<int> loop_xor(n);
	for (const auto [x, y, c] : edges) {
		if ((!doubling[x].empty() && doubling[x][0] == y) || (!doubling[y].empty() && doubling[y][0] == x)) continue;
		const auto value{ xor_from_root[x] ^ xor_from_root[y] ^ c };
		int a{ x }, b{ y };
		while (a != b) {
			if (depth[a] > depth[b]) {
				loop_xor[a] = value;
				a = doubling[a][0];
			}
			else {
				loop_xor[b] = value;
				b = doubling[b][0];
			}
		}
	}
	std::vector<f2_vec> f2_vecs(n);
	for (const auto node : queue) {
		if (!doubling[node].empty()) {
			f2_vecs[node] = f2_vecs[doubling[node][0]];
		}
		if (loop_xor[node]) {
			f2_vecs[node].add(depth[node], loop_xor[node]);
		}
	}
	const auto calc_lca = [&depth, &doubling](int a, int b) {
		if (depth[a] < depth[b]) std::swap(a, b);
		const auto diff = depth[a] - depth[b];
		for (auto i = 0; i < doubling[a].size(); ++i) {
			if ((diff >> i) & 1) {
				a = doubling[a][i];
			}
		}
		if (a == b) return a;
		for (int i = doubling[a].size() - 1; i >= 0; --i) {
			if (i < doubling[a].size() && doubling[a][i] != doubling[b][i]) {
				a = doubling[a][i];
				b = doubling[b][i];
			}
		}
		return doubling[a][0];
	};
	const auto merge = [](const std::vector<std::pair<int, int>>& a, const std::vector<std::pair<int, int>>& b) {
		std::array<int, BIT_COUNT> array{};
		for (const auto v : a) {
			add(array, v.second);
		}
		for (const auto v : b) {
			add(array, v.second);
		}
		return array;
	};
	for (const auto [x, y, k] : queries) {
		const auto lca = calc_lca(x, y);
		const auto to_x = f2_vecs[x].filter(depth[lca]);
		const auto to_y = f2_vecs[y].filter(depth[lca]);
		const auto merged = merge(to_x, to_y);
		const auto vec_size = std::count_if(merged.begin(), merged.end(), [](const auto v) { return v != 0; });
		if ((1 << vec_size) <= k) {
			std::cout << "-1\n";
		}
		else {
			int result{ xor_from_root[x] ^ xor_from_root[y] };
			int rank = vec_size;
			for (auto i = 0; i < BIT_COUNT; ++i) {
				if (merged[i] == 0) continue;
				if (((k >> --rank) & 1) != ((result >> (BIT_COUNT - i - 1)) & 1)) {
					result ^= merged[i];
				}
			}
			std::cout << result << '\n';
		}
	}
}
int main() {
	int n, m; std::cin >> n >> m;
	std::vector<std::tuple<int, int, int>> edges(m);
	for (auto& [x, y, c] : edges) {
		std::cin >> x >> y >> c; --x; --y;
	}
	int q; std::cin >> q;
	std::vector<std::tuple<int, int, int>> queries(q);
	for (auto& [x, y, k] : queries) {
		std::cin >> x >> y >> k; --x; --y; --k;
	}
	solve(n, m, edges, q, queries);
}
accuracy: 1
time_ms: 1240
memory_kb: 80544
final_rank: 2

code_snippet: #include <vector>
#include <algorithm>
#include <cmath>
#include <numeric>
#include <iostream>
#include <iomanip>
#include <unordered_map>
#include <climits>
#include <queue>
#include <stack>
#include <random>
#include <set>
#include <cassert>
#include <iterator>
#include <type_traits>
#include <map>
#include <array>
#include <sstream>
#include <bitset>
#include <fstream>

constexpr int BIT_COUNT = 30;
class f2_vec {
	struct Pair {
		int first, second;
		Pair operator^(const Pair other) const {
			return { first ^ other.first, second ^ other.second };
		}
		Pair& operator^=(const Pair other) {
			first ^= other.first;
			second ^= other.second;
			return *this;
		}
	};
public:
	std::array<Pair, BIT_COUNT> vec;
	std::vector<std::pair<int, int>> used;
	void add(const int depth, const int value) {
		Pair pair{ 0, value };
		for (auto i = 0; i < BIT_COUNT; ++i) {
			if ((pair.second >> (BIT_COUNT - i - 1)) & 1) {
				if (vec[i].second) {
					pair ^= vec[i];
				}
				else {
					vec[i].first = 1 << used.size();
					vec[i].second = pair.second;
					used.emplace_back(depth, value);
					return;
				}
			}
		}
		int min_vec{ -1 };
		for (auto i = 0; i < used.size(); ++i) {
			if ((pair.first >> i) & 1) {
				if (min_vec == -1 || vec[min_vec].first > vec[i].first) {
					min_vec = i;
				}
			}
		}
		used[min_vec] = std::make_pair(depth, value);
		const auto mask = pair.first ^ (1 << min_vec);
		for (auto i = 0; i < BIT_COUNT; ++i) {
			if ((vec[i].first >> min_vec) & 1) {
				vec[i].first ^= mask;
			}
		}
	}
	std::vector<std::pair<int, int>> filter(const int depth) const {
		std::vector<std::pair<int, int>> result;
		std::copy_if(used.begin(), used.end(), std::back_inserter(result), [depth](const auto pair) {return pair.first > depth; });
		return result;
	}
};

void solve(const int n, const int m, const std::vector<std::tuple<int, int, int>> edges, const int q, const std::vector<std::tuple<int, int, int>> queries) {
	std::vector<std::vector<std::pair<int, int>>> graph(n);
	for (const auto [x, y, c] : edges) {
		graph[x].emplace_back(y, c);
		graph[y].emplace_back(x, c);
	}
	std::vector<int> queue(n), depth(n, -1), xor_from_root(n);
	std::vector<std::vector<int>> doubling(n);
	{
		int begin{ 0 }, end{ 0 };
		queue[end++] = 0;
		depth[0] = 0;
		while (begin < end) {
			const auto current = queue[begin++];
			for (const auto [next, c] : graph[current]) {
				if (depth[next] != -1) continue;
				depth[next] = depth[current] + 1;
				xor_from_root[next] = c ^ xor_from_root[current];
				queue[end++] = next;
				auto& ancestor = doubling[next];
				ancestor.push_back(current);
				while (ancestor.size() <= doubling[ancestor.back()].size()) {
					ancestor.push_back(doubling[ancestor.back()][ancestor.size() - 1]);
				}
			}
		}
	}
	std::vector<int> loop_xor(n);
	for (const auto [x, y, c] : edges) {
		if ((!doubling[x].empty() && doubling[x][0] == y) || (!doubling[y].empty() && doubling[y][0] == x)) continue;
		const auto value{ xor_from_root[x] ^ xor_from_root[y] ^ c };
		int a{ x }, b{ y };
		while (a != b) {
			if (depth[a] > depth[b]) {
				loop_xor[a] = value;
				a = doubling[a][0];
			}
			else {
				loop_xor[b] = value;
				b = doubling[b][0];
			}
		}
	}
	std::vector<f2_vec> f2_vecs(n);
	for (const auto node : queue) {
		if (!doubling[node].empty()) {
			f2_vecs[node] = f2_vecs[doubling[node][0]];
		}
		if (loop_xor[node]) {
			f2_vecs[node].add(depth[node], loop_xor[node]);
		}
	}
	const auto calc_lca = [&depth, &doubling](int a, int b) {
		if (depth[a] < depth[b]) std::swap(a, b);
		const auto diff = depth[a] - depth[b];
		for (auto i = 0; i < doubling[a].size(); ++i) {
			if ((diff >> i) & 1) {
				a = doubling[a][i];
			}
		}
		if (a == b) return a;
		for (int i = doubling[a].size() - 1; i >= 0; --i) {
			if (i < doubling[a].size() && doubling[a][i] != doubling[b][i]) {
				a = doubling[a][i];
				b = doubling[b][i];
			}
		}
		return doubling[a][0];
	};
	const auto add = [](std::array<int, BIT_COUNT>& arr, const int value) {
		int v{ value };
		for (auto i = 0; i < BIT_COUNT && v != 0; ++i) {
			if ((v >> (BIT_COUNT - i - 1)) & 1) {
				if (arr[i]) {
					v ^= arr[i];
				}
				else {
					arr[i] = v;
					return;
				}
			}
		}
	};
	const auto merge = [&add](const std::vector<std::pair<int, int>>& a, const std::vector<std::pair<int, int>>& b) {
		std::array<int, BIT_COUNT> array{};
		for (const auto v : a) {
			add(array, v.second);
		}
		for (const auto v : b) {
			add(array, v.second);
		}
		return array;
	};
	for (const auto [x, y, k] : queries) {
		const auto lca = calc_lca(x, y);
		const auto to_x = f2_vecs[x].filter(depth[lca]);
		const auto to_y = f2_vecs[y].filter(depth[lca]);
		const auto merged = merge(to_x, to_y);
		const auto vec_size = std::count_if(merged.begin(), merged.end(), [](const auto v) { return v != 0; });
		if ((1 << vec_size) <= k) {
			std::cout << "-1\n";
		}
		else {
			int result{ xor_from_root[x] ^ xor_from_root[y] };
			int rank = vec_size;
			for (auto i = 0; i < BIT_COUNT; ++i) {
				if (merged[i] == 0) continue;
				if (((k >> --rank) & 1) != ((result >> (BIT_COUNT - i - 1)) & 1)) {
					result ^= merged[i];
				}
			}
			std::cout << result << '\n';
		}
	}
}
int main() {
	int n, m; std::cin >> n >> m;
	std::vector<std::tuple<int, int, int>> edges(m);
	for (auto& [x, y, c] : edges) {
		std::cin >> x >> y >> c; --x; --y;
	}
	int q; std::cin >> q;
	std::vector<std::tuple<int, int, int>> queries(q);
	for (auto& [x, y, k] : queries) {
		std::cin >> x >> y >> k; --x; --y; --k;
	}
	solve(n, m, edges, q, queries);
}
accuracy: 0.31746031746031744
time_ms: 460
memory_kb: 77220
final_rank: 15

code_snippet: #include <vector>
#include <algorithm>
#include <cmath>
#include <numeric>
#include <iostream>
#include <iomanip>
#include <unordered_map>
#include <climits>
#include <queue>
#include <stack>
#include <random>
#include <set>
#include <cassert>
#include <iterator>
#include <type_traits>
#include <map>
#include <array>
#include <sstream>
#include <bitset>

constexpr int BIT_COUNT = 30;
class f2_vec {
	struct Pair {
		int first, second;
		Pair operator^(const Pair other) const {
			return { first ^ other.first, second ^ other.second };
		}
		Pair& operator^=(const Pair other) {
			first ^= other.first;
			second ^= other.second;
			return *this;
		}
	};
public:
	std::array<Pair, BIT_COUNT> vec;
	std::vector<std::pair<int, int>> used;
	void add(const int depth, const int value) {
		Pair pair{ 0, value };
		for (auto i = 0; i < BIT_COUNT; ++i) {
			if ((pair.second >> (BIT_COUNT - i - 1)) & 1) {
				if (vec[i].second) {
					pair ^= vec[i];
				}
				else {
					vec[i].first = 1 << used.size();
					vec[i].second = pair.second;
					used.emplace_back(depth, value);
					return;
				}
			}
		}
		int min_vec{ -1 };
		for (auto i = 0; i < used.size(); ++i) {
			if ((pair.first >> i) & 1) {
				if (min_vec == -1 || vec[min_vec].first > vec[i].first) {
					min_vec = i;
				}
			}
		}
		used[min_vec] = std::make_pair(depth, value);
		const auto mask = pair.first ^ (1 << min_vec);
		for (auto i = 0; i < BIT_COUNT; ++i) {
			if ((vec[i].first >> min_vec) & 1) {
				vec[i].first ^= mask;
			}
		}
	}
	std::vector<std::pair<int, int>> filter(const int depth) const {
		std::vector<std::pair<int, int>> result;
		std::copy_if(used.begin(), used.end(), std::back_inserter(result), [depth](const auto pair) {return pair.first > depth; });
		return result;
	}
};

void solve(const int n, const int m, const std::vector<std::tuple<int, int, int>> edges, const int q, const std::vector<std::tuple<int, int, int>> queries) {
	std::vector<std::vector<std::pair<int, int>>> graph(n);
	for (const auto [x, y, c] : edges) {
		graph[x].emplace_back(y, c);
		graph[y].emplace_back(x, c);
	}
	std::vector<int> queue(n), depth(n, -1), xor_from_root(n);
	std::vector<std::vector<int>> doubling(n);
	{
		int begin{ 0 }, end{ 0 };
		queue[end++] = 0;
		depth[0] = 0;
		while (begin < end) {
			const auto current = queue[begin++];
			for (const auto [next, c] : graph[current]) {
				if (depth[next] != -1) continue;
				depth[next] = depth[current] + 1;
				xor_from_root[next] = c ^ xor_from_root[current];
				queue[end++] = next;
				auto& ancestor = doubling[next];
				ancestor.push_back(current);
				while (ancestor.size() <= doubling[ancestor.back()].size()) {
					ancestor.push_back(doubling[ancestor.back()][ancestor.size() - 1]);
				}
			}
		}
	}
	std::vector<int> loop_xor(n);
	for (const auto [x, y, c] : edges) {
		if ((!doubling[x].empty() && doubling[x][0] == y) || (!doubling[y].empty() && doubling[y][0] == x)) continue;
		const auto value{ xor_from_root[x] ^ xor_from_root[y] ^ c };
		int a{ x }, b{ y };
		while (a != b) {
			if (depth[a] > depth[b]) {
				loop_xor[a] = value;
				a = doubling[a][0];
			}
			else {
				loop_xor[b] = value;
				b = doubling[b][0];
			}
		}
	}
	std::vector<f2_vec> f2_vecs(n);
	for (const auto node : queue) {
		if (!doubling[node].empty()) {
			f2_vecs[node] = f2_vecs[doubling[node][0]];
		}
		if (loop_xor[node]) {
			f2_vecs[node].add(depth[node], loop_xor[node]);
		}
	}
	const auto calc_lca = [&depth, &doubling](int a, int b) {
		if (depth[a] < depth[b]) std::swap(a, b);
		const auto diff = depth[a] - depth[b];
		for (auto i = 0; i < doubling[a].size(); ++i) {
			if ((diff >> i) & 1) {
				a = doubling[a][i];
			}
		}
		if (a == b) return a;
		for (int i = doubling[a].size() - 1; i >= 0; --i) {
			if (i < doubling[a].size() && doubling[a][i] != doubling[b][i]) {
				a = doubling[a][i];
				b = doubling[b][i];
			}
		}
		return doubling[a][0];
	};
	const auto add = [](std::array<int, BIT_COUNT>& arr, const int value) {
		int v{ value };
		for (auto i = 0; i < BIT_COUNT && v != 0; ++i) {
			if ((v >> i) & 1) {
				if (arr[i]) {
					v ^= arr[i];
				}
				else {
					arr[i] = v;
					return;
				}
			}
		}
	};
	const auto merge = [&add](const std::vector<std::pair<int, int>>& a, const std::vector<std::pair<int, int>>& b) {
		std::array<int, BIT_COUNT> array{};
		for (const auto v : a) {
			add(array, v.second);
		}
		for (const auto v : b) {
			add(array, v.second);
		}
		return array;
	};
	for (const auto [x, y, k] : queries) {
		const auto lca = calc_lca(x, y);
		const auto to_x = f2_vecs[x].filter(depth[lca]);
		const auto to_y = f2_vecs[y].filter(depth[lca]);
		const auto merged = merge(to_x, to_y);
		const auto vec_size = std::count_if(merged.begin(), merged.end(), [](const auto v) { return v != 0; });
		if ((1 << vec_size) <= k) {
			std::cout << "-1\n";
		}
		else {
			int result{ xor_from_root[x] ^ xor_from_root[y] };
			int rank = vec_size;
			for (auto i = 0; i < BIT_COUNT; ++i) {
				if (merged[i] == 0) continue;
				if (((k >> --rank) & 1) != ((result >> (BIT_COUNT - i - 1)) & 1)) {
					result ^= merged[i];
				}
			}
			std::cout << result << '\n';
		}
	}
}
int main() {
	int n, m; std::cin >> n >> m;
	std::vector<std::tuple<int, int, int>> edges(m);
	for (auto& [x, y, c] : edges) {
		std::cin >> x >> y >> c; --x; --y;
	}
	int q; std::cin >> q;
	std::vector<std::tuple<int, int, int>> queries(q);
	for (auto& [x, y, k] : queries) {
		std::cin >> x >> y >> k; --x; --y; --k;
	}
	solve(n, m, edges, q, queries);
}
accuracy: 0.09523809523809523
time_ms: 340
memory_kb: 57164
final_rank: 18

code_snippet: #include <deque>
#include <vector>
#include <iostream>
#include <string>
#include <numeric>
#include <cmath>
#include <queue>
#include <tuple>
#include <algorithm>
#include <initializer_list>
#include <array>
#include <cassert>
void solve(const int n, const int m, const std::vector<std::tuple<int, int, int>> edges, const int q, const std::vector<std::tuple<int, int, int>> queries);
int main() {
	int n, m; std::cin >> n >> m;
	std::vector<std::tuple<int, int, int>> edges(m);
	for (auto& [x, y, c] : edges) {
		std::cin >> x >> y >> c; --x; --y;
	}
	int q; std::cin >> q;
	std::vector<std::tuple<int, int, int>> queries(q);
	for (auto& [x, y, c] : queries) {
		std::cin >> x >> y >> c; --x; --y; --c;
	}
	solve(n, m, edges, q, queries);
}
constexpr int MAX_BIT = 29;
class f2_vec {
	std::array<std::pair<int, int>, MAX_BIT + 1> vec{};
	std::array<int, MAX_BIT + 1> normalized{};
public:
	void add(const int depth, const int value) {
		int v{ value };
		for (auto i = 0; i <= MAX_BIT && v != 0; ++i) {
			if ((v >> (MAX_BIT - i)) & 1) {
				if (normalized[i] == 0) {
					normalized[i] = v;
					vec[i] = std::make_pair(depth, value);
					return;
				}
				else {
					v ^= normalized[i];
				}
			}
		}
		v = value;
		int min_vec{ -1 };
		for (auto i = 0; i <= MAX_BIT && v != 0; ++i) {
			if ((v >> (MAX_BIT - i)) & 1) {
				if (min_vec == -1 || vec[min_vec].first > vec[i].first) {
					min_vec = i;
				}
				v ^= normalized[i];
			}
		}
		assert(vec[min_vec].first < depth);
		vec[min_vec].first = depth;
	}
	std::vector<int> filter_vec(const int depth) const {
		std::vector<int> result;
		for (const auto [d, v] : vec) {
			if (d > depth) {
				result.push_back(v);
			}
		}
		return result;
	}
};
void solve(const int n, const int m, const std::vector<std::tuple<int, int, int>> edges, const int q, const std::vector<std::tuple<int, int, int>> queries) {
	std::vector<std::vector<std::pair<int, int>>> graph(n);
	for (const auto [x, y, c] : edges) {
		graph[x].emplace_back(y, c);
		graph[y].emplace_back(x, c);
	}
	std::vector<int> depth(n, -1), xor_from_root(n, 0);
	std::vector<std::vector<int>> doubling(n);
	std::vector<f2_vec> f2_vecs(n);
	std::vector<int> queue(n); 
	depth[0] = 0;
	queue[0] = 0;
	int begin{ 0 }, end{ 1 };
	while (begin < end) {
		const auto current = queue[begin++];
		for (const auto [next, c] : graph[current]) {
			if (depth[next] != -1) continue;
			xor_from_root[next] = c ^ xor_from_root[current];
			depth[next] = depth[current] + 1;
			queue[end++] = next;
			f2_vecs[next] = f2_vecs[current];
			f2_vecs[next].add(depth[next], c);
			auto& ancestor = doubling[next];
			ancestor.push_back(current);
			while (ancestor.size() <= doubling[ancestor.back()].size()) {
				ancestor.push_back(doubling[ancestor.back()][ancestor.size() - 1]);
			}
		}
	}
	std::vector<int> loop_xor(n);
	for (const auto [x, y, c] : edges) {
		if ((!doubling[x].empty() && doubling[x][0] == y) || (!doubling[y].empty() && doubling[y][0] == x)) continue;
		const auto xor_value = xor_from_root[x] ^ xor_from_root[y] ^ c;
		int a = x, b = y;
		while (a != b) {
			if (depth[a] >= depth[b]) {
				loop_xor[a] = xor_value;
				a = doubling[a][0];
			}
			else {
				loop_xor[b] = xor_value;
				b = doubling[b][0];
			}
		}
	}
	for (const auto node : queue) {
		if (!doubling[node].empty()) {
			f2_vecs[node] = f2_vecs[doubling[node][0]];
		}
		if (loop_xor[node] != 0) {
			f2_vecs[node].add(depth[node], loop_xor[node]);
		}
	}
	const auto lca = [&doubling, &depth](int a, int b) {
		if (depth[a] < depth[b]) {
			std::swap(a, b);
		}
		const int diff = depth[a] - depth[b];
		for (auto i = 0; i < doubling[a].size(); ++i) {
			if ((diff >> i) & 1) {
				a = doubling[a][i];
			}
		}
		if (a == b) return a;
		for (int i = doubling[a].size() - 1; i >= 0; --i) {
			if (i < doubling[a].size() && doubling[a][i] != doubling[b][i]) {
				a = doubling[a][i];
				b = doubling[b][i];
			}
		}
		return doubling[a][0];
	};
	const auto merge = [](const std::vector<int>& v1, const std::vector<int>& v2) {
		std::array<int, MAX_BIT + 1> result{ 0 };
		for (auto v : v1) {
			for (auto i = 0; i <= MAX_BIT && v != 0; ++i) {
				if ((v >> (MAX_BIT - i)) & 1) {
					if (result[i] == 0) {
						result[i] = v;
						break;
					}
					else {
						v ^= result[i];
					}
				}
			}
		}
		for (auto v : v2) {
			for (auto i = 0; i <= MAX_BIT && v != 0; ++i) {
				if ((v >> (MAX_BIT - i)) & 1) {
					if (result[i] == 0) {
						result[i] = v;
						break;
					}
					else {
						v ^= result[i];
					}
				}
			}
		}
		return result;
	};
	for (const auto [x, y, k] : queries) {
		const auto ancestor = lca(x, y);
		const auto to_x = f2_vecs[x].filter_vec(depth[ancestor]);
		const auto to_y = f2_vecs[y].filter_vec(depth[ancestor]);
		const auto merged = merge(to_x, to_y);
		const auto vec_size = merged.size() - std::count(merged.begin(), merged.end(), 0);
		if ((1 << vec_size) <= k) {
			std::cout << "-1\n";
		}
		else {
			int result{ xor_from_root[x] ^ xor_from_root[y] };
			int rank = vec_size;
			for (int i = 0; i <= MAX_BIT; ++i) {
				if (merged[i] == 0) continue;
				if (((k >> --rank) & 1) != ((result >> (MAX_BIT - i)) & 1)) {
					result ^= merged[i];
				}
			}
			std::cout << result << '\n';
		}
	}
}
accuracy: 0.31746031746031744
time_ms: 660
memory_kb: 64180
final_rank: 14

code_snippet: #include <bits/stdc++.h>
using namespace std;

using ll=long long;
//#define int ll

#define rng(i,a,b) for(int i=int(a);i<int(b);i++)
#define rep(i,b) rng(i,0,b)
#define gnr(i,a,b) for(int i=int(b)-1;i>=int(a);i--)
#define per(i,b) gnr(i,0,b)
#define pb push_back
#define eb emplace_back
#define a first
#define b second
#define bg begin()
#define ed end()
#define all(x) x.bg,x.ed
#define si(x) int(x.size())
#ifdef LOCAL
#define dmp(x) cerr<<__LINE__<<" "<<#x<<" "<<x<<endl
#else
#define dmp(x) void(0)
#endif

template<class t,class u> void chmax(t&a,u b){if(a<b)a=b;}
template<class t,class u> void chmin(t&a,u b){if(b<a)a=b;}

template<class t> using vc=vector<t>;
template<class t> using vvc=vc<vc<t>>;

using pi=pair<int,int>;
using vi=vc<int>;

template<class t,class u>
ostream& operator<<(ostream& os,const pair<t,u>& p){
	return os<<"{"<<p.a<<","<<p.b<<"}";
}

template<class t> ostream& operator<<(ostream& os,const vc<t>& v){
	os<<"{";
	for(auto e:v)os<<e<<",";
	return os<<"}";
}

#define mp make_pair
#define mt make_tuple
#define one(x) memset(x,-1,sizeof(x))
#define zero(x) memset(x,0,sizeof(x))
#ifdef LOCAL
void dmpr(ostream&os){os<<endl;}
template<class T,class... Args>
void dmpr(ostream&os,const T&t,const Args&... args){
	os<<t<<" ";
	dmpr(os,args...);
}
#define dmp2(...) dmpr(cerr,__LINE__,##__VA_ARGS__)
#else
#define dmp2(...) void(0)
#endif

using uint=unsigned;
using ull=unsigned long long;

template<class t,size_t n>
ostream& operator<<(ostream&os,const array<t,n>&a){
	return os<<vc<t>(all(a));
}

template<int i,class T>
void print_tuple(ostream&,const T&){
}

template<int i,class T,class H,class ...Args>
void print_tuple(ostream&os,const T&t){
	if(i)os<<",";
	os<<get<i>(t);
	print_tuple<i+1,T,Args...>(os,t);
}

template<class ...Args>
ostream& operator<<(ostream&os,const tuple<Args...>&t){
	os<<"{";
	print_tuple<0,tuple<Args...>,Args...>(os,t);
	return os<<"}";
}

template<class t>
void print(t x,int suc=1){
	cout<<x;
	if(suc==1)
		cout<<"\n";
	if(suc==2)
		cout<<" ";
}

ll read(){
	ll i;
	cin>>i;
	return i;
}

vi readvi(int n,int off=0){
	vi v(n);
	rep(i,n)v[i]=read()+off;
	return v;
}

pi readpi(int off=0){
	int a,b;cin>>a>>b;
	return pi(a+off,b+off);
}

template<class T>
void print(const vector<T>&v,int suc=1){
	rep(i,v.size())
		print(v[i],i==int(v.size())-1?suc:2);
}

string readString(){
	string s;
	cin>>s;
	return s;
}

template<class T>
T sq(const T& t){
	return t*t;
}

//#define CAPITAL
void yes(bool ex=true){
	#ifdef CAPITAL
	cout<<"YES"<<"\n";
	#else
	cout<<"Yes"<<"\n";
	#endif
	if(ex)exit(0);
	#ifdef LOCAL
	cout.flush();
	#endif
}
void no(bool ex=true){
	#ifdef CAPITAL
	cout<<"NO"<<"\n";
	#else
	cout<<"No"<<"\n";
	#endif
	if(ex)exit(0);
	#ifdef LOCAL
	cout.flush();
	#endif
}
void possible(bool ex=true){
	#ifdef CAPITAL
	cout<<"POSSIBLE"<<"\n";
	#else
	cout<<"Possible"<<"\n";
	#endif
	if(ex)exit(0);
	#ifdef LOCAL
	cout.flush();
	#endif
}
void impossible(bool ex=true){
	#ifdef CAPITAL
	cout<<"IMPOSSIBLE"<<"\n";
	#else
	cout<<"Impossible"<<"\n";
	#endif
	if(ex)exit(0);
	#ifdef LOCAL
	cout.flush();
	#endif
}

constexpr ll ten(int n){
	return n==0?1:ten(n-1)*10;
}

const ll infLL=LLONG_MAX/3;

#ifdef int
const int inf=infLL;
#else
const int inf=INT_MAX/2-100;
#endif

int topbit(signed t){
	return t==0?-1:31-__builtin_clz(t);
}
int topbit(ll t){
	return t==0?-1:63-__builtin_clzll(t);
}
int botbit(signed a){
	return a==0?32:__builtin_ctz(a);
}
int botbit(ll a){
	return a==0?64:__builtin_ctzll(a);
}
int popcount(signed t){
	return __builtin_popcount(t);
}
int popcount(ll t){
	return __builtin_popcountll(t);
}
bool ispow2(int i){
	return i&&(i&-i)==i;
}
ll mask(int i){
	return (ll(1)<<i)-1;
}

bool inc(int a,int b,int c){
	return a<=b&&b<=c;
}

template<class t> void mkuni(vc<t>&v){
	sort(all(v));
	v.erase(unique(all(v)),v.ed);
}

ll rand_int(ll l, ll r) { //[l, r]
	#ifdef LOCAL
	static mt19937_64 gen;
	#else
	static mt19937_64 gen(chrono::steady_clock::now().time_since_epoch().count());
	#endif
	return uniform_int_distribution<ll>(l, r)(gen);
}

template<class t>
void myshuffle(vc<t>&a){
	rep(i,si(a))swap(a[i],a[rand_int(0,i)]);
}

template<class t>
int lwb(const vc<t>&v,const t&a){
	return lower_bound(all(v),a)-v.bg;
}

vvc<int> readGraph(int n,int m){
	vvc<int> g(n);
	rep(i,m){
		int a,b;
		cin>>a>>b;
		//sc.read(a,b);
		a--;b--;
		g[a].pb(b);
		g[b].pb(a);
	}
	return g;
}

vvc<int> readTree(int n){
	return readGraph(n,n-1);
}


//KUPC 2020 C
//重心分解してクエリに答える
//クエリ (a,b) に対しては，LCA から a までのパスと，LCA から b までのパスの情報を使って答える．
//パスの情報は N に保存される
//N() が単位元的な役割
//N.extend(e) で辺 e を使って下に下って新しいノードを作る
//N 同士のマージをやると計算量が壊れるが extend なら．．．というときに使える
template<class E,class N>
struct cdecomp{
	const vvc<E>&g;
	int n;
	vi rem;
	
	int ts(int v,int p){
		int res=1;
		for(auto e:g[v])if(e!=p&&!rem[e])
			res+=ts(e,v);
		return res;
	}
	int fc(int v,int p,int s){
		int ret=1,mx=0;
		for(auto e:g[v])if(e!=p&&!rem[e]){
			int f=fc(e,v,s);
			if(f<=0)
				return f;
			else{
				ret+=f;
				mx=max(mx,f);
			}
		}
		mx=max(mx,s-ret);
		if(mx*2<=s)
			return -v;
		else
			return ret;
	}
	
	cdecomp(const vvc<E>&gg):g(gg),n(g.size()),rem(n){
	}
	
	vc<N> buf;
	vi tp;
	vvc<tuple<int,int,int>> bucket;
	
	void dfs1(int v,int p,int i,N cur){
		buf[v]=cur;
		tp[v]=i;
		for(auto e:g[v])if(e!=p&&!rem[e]){
			dfs1(e,v,i==-1?e:i,cur.extend(e));
		}
	}
	
	template<class F>
	void con(int r,const vc<tuple<int,int,int>>&qs,F f){
		r=-fc(r,-1,ts(r,-1));
		
		dfs1(r,-1,-1,N());
		for(const auto&w:qs){
			int a,b,i;tie(a,b,i)=w;
			if(tp[a]!=tp[b]){
				f(i,buf[a],buf[b],r);
			}else{
				bucket[tp[a]].pb(w);
			}
		}
		
		rem[r]=1;
		for(auto e:g[r])if(!rem[e]){
			vc<tuple<int,int,int>> tmp;
			tmp.swap(bucket[e]);
			con(e,tmp,f);
		}
	}
	
	//f(idx,N lf,N rt,int lca) がよばれる
	//[qs[i].a,lca) が lf に，[qs[i].b,lca) が rt に入るらしい
	template<class F>
	void slv(const vc<pi>&qs,F f){
		fill(all(rem),0);
		buf.resize(n);
		tp.resize(n);
		bucket.resize(n);
		
		vc<tuple<int,int,int>> tmp(si(qs));
		rep(i,si(qs))
			tmp[i]=mt(qs[i].a,qs[i].b,i);
		con(0,tmp,f);
	}
};

//KUPC 2020 C
//非連結な場合はverifyされてない

//多重辺なしの cactus を分解する
//cs にサイクルに使われた辺の集合が入る
//順番は，dfs 木を下る->後退辺で上がる，の順
//cs のサイズが 2 のときはシンプルに辺があるだけ
//連結性を仮定せず
template<class E>
struct cactus{
	const vvc<E>&g;
	const int n;
	vvc<E> cs;
	vi vis,par,u;
	vc<E> come;
	vc<bool> done;
	void dfs(int v,int p,E co){
		assert(vis[v]==0);
		vis[v]=1;
		par[v]=p;
		come[v]=co;
		E gopar;
		for(auto e:g[v]){
			if(e==p){
				gopar=e;
			}else if(vis[e]==0){
				dfs(e,v,e);
			}else if(vis[e]==1){
				int x=v;
				vc<E> z{e};
				while(x!=e){
					assert(!done[x]);
					done[x]=true;
					z.pb(come[x]);
					x=par[x];
				}
				reverse(all(z));
				cs.pb(z);
			}
		}
		if(p!=-1&&!done[v]){
			done[v]=true;
			cs.pb({co,gopar});
		}
		vis[v]=2;
	}
	cactus(const vvc<E>&gg):g(gg),n(g.size()),vis(n),par(n),u(n),come(n),done(n){
		rep(i,n)if(!vis[i])
			dfs(i,-1,E());
	}
};
struct E{
	int to,cost;
	operator int()const{return to;}
};

const int nmax=200010;
const int L=30;

bool isc[nmax];
int cv[nmax];

struct N{
	int s,vs[L],off;
	N(){
		s=0;
		off=0;
	}
	void add(int v){
		rep(i,s)chmin(v,v^vs[i]);
		if(v)vs[s++]=v;
	}
	N extend(const E&e)const{
		N res=*this;
		res.off^=e.cost;
		if(cv[e.to]!=-1)
			res.add(cv[e.to]);
		return res;
	}
	void show(){
		dmp(s);
		dmp(vi(vs,vs+s));
		dmp(off);
	}
	int getans(int k){
		if(k>=(1<<s))return -1;
		sort(vs,vs+s);
		rep(i,s){
			rng(j,i+1,s){
				chmin(vs[j],vs[j]^vs[i]);
			}
			chmin(off,off^vs[i]);
		}
		int res=off;
		rep(i,s)if(k&1<<i)res^=vs[i];
		return res;
	}
};

signed main(){
	cin.tie(0);
	ios::sync_with_stdio(0);
	cout<<fixed<<setprecision(20);
	
	int n;cin>>n;
	int m;cin>>m;
	vvc<E> g(n);
	rep(_,m){
		int a,b,c;cin>>a>>b>>c;
		a--;b--;
		g[a].pb({b,c});
		g[b].pb({a,c});
	}
	cactus<E> cc(g);
	
	int s=n+si(cc.cs);
	vvc<E> t(s);
	auto ae=[&](int a,int b,int c){
		t[a].pb({b,c});
		t[b].pb({a,c});
	};
	one(cv);
	dmp(cc.cs);
	rep(i,si(cc.cs)){
		int cur=0;
		for(auto e:cc.cs[i]){
			cur^=e.cost;
			ae(e,n+i,cur);
		}
		cv[n+i]=cur;
	}
	
	cdecomp<E,N> cd(t);
	
	int qnum;cin>>qnum;
	vc<pi> ab(qnum);
	vi ks(qnum);
	rep(i,qnum){
		int a,b;cin>>a>>b;
		a--;b--;
		ab[i]=pi(a,b);
		int k;cin>>k;
		ks[i]=k-1;
	}
	
	vi ans(qnum);
	auto slv=[&](int dst,N x,N y,int lca){
		x.off^=y.off;
		rep(i,y.s)x.add(y.vs[i]);
		if(cv[lca]!=-1)x.add(cv[lca]);
		ans[dst]=x.getans(ks[dst]);
	};
	cd.slv(ab,slv);
	
	for(auto v:ans)print(v);
}
accuracy: 1
time_ms: 860
memory_kb: 96596
final_rank: 1

code_snippet: #include <bits/stdc++.h>
using namespace std;

enum BLOCK_TREE_TYPE { BLOCK_CUT_TREE = 0, BRIDGE_TREE = 1 };

// input:
// n: number of nodes.
// gr: adjacentcy lists of the graph.
// Usage:
// auto bc_tree = build_block_tree<BLOCK_CUT_TREE>(n, gr);
// auto bridge_tree = build_block_tree<BRIDGE_TREE>(n, gr);
template<BLOCK_TREE_TYPE tree_type, int node_start_id = 1, class Graph = vector<int>[]>
vector<vector<int>> build_block_tree(int n, const Graph& gr) {
    n += node_start_id;
    vector<int> low(n), num(n, -1), stk;
    vector<vector<int>> bc_tree(n);
    
    auto new_edge = [&](int u, int v) {
        bc_tree[u].push_back(v);
        bc_tree[v].push_back(u);
    };
    
    auto new_component = [&](int last_node = -1) {
        int comp_id = (int)bc_tree.size();
        bc_tree.emplace_back();
        for (; stk.size() and (!bc_tree.back().size() or bc_tree.back().back() != last_node); stk.pop_back())
            new_edge(comp_id, stk.back());
        return comp_id;
    };
    
    int counter = 0;
    function<void(int, int)> dfs = [&](int u, int p) {
        low[u] = num[u] = counter++;
        stk.push_back(u);
        for (auto v: gr[u]) {
            if (v == p) continue;
            if (num[v] != -1) {
                low[u] = min(low[u], num[v]);
                continue;
            }
            dfs(v, u);
            low[u] = min(low[u], low[v]);
            if (low[v] < num[u] + tree_type) continue;
            int comp_id = new_component(v);
            new_edge(u, tree_type == BLOCK_CUT_TREE ? comp_id : v);
        }
    };
    
    for (int i = node_start_id; i < n; ++i) {
        if (num[i] == -1) {
            dfs(i, -1);
            if (tree_type == BRIDGE_TREE) new_component();
        }
        // make the component id to be the first element. can be removed if not needed
        for (int& f: bc_tree[i]) if (f > n) {
            swap(bc_tree[i][0], f);
            break;
        }
        // modification?: move all non-bridge/cut point to the end for faster iterating through adj component.
    }
    return bc_tree;
}

/**
 * Author: Tran Quang Loc (darkkcyan)
 * Tested with https://github.com/quangloc99/CompetitiveProgramming/blob/master/Codeforces/CF342-D2-E.cpp
 */
template<class Graph = vector<int>*, int node_start_id = 1>
struct centroid_decomposer {
    int n;
    const Graph& gr;
    vector<bool> mark;
    vector<int> child_cnt;

    centroid_decomposer(int n_, const Graph& gr_)
        : n(n_ + node_start_id), gr(gr_), mark(n), child_cnt(n) {}
    
    void reset(int n_ = -1) {
        if (n != -1) n = n_ + node_start_id;
        mark.assign(n, 0);
        child_cnt.assign(n, 0);
    }

    int dfs_count_child(int u, int p) {
        child_cnt[u] = 1;
        for (auto v: gr[u]) if (v != p and !mark[v])
            child_cnt[u] += dfs_count_child(v, u);
        return child_cnt[u];
    }

    // possible modification: return a pair which contain a new centroid and
    // the size of its region (the `total` variable)
    int find_centroid(int u, int p = -1, int total = -1) {
        assert(u != -1);
        if (p == -1) total = dfs_count_child(u, u);
        pair<int, int> big_child(total - child_cnt[u], -1);
        for (auto v: gr[u]) if (!mark[v] and v != p)
            big_child = max(big_child, {child_cnt[v], v});
        if (big_child.first > total / 2) 
            return find_centroid(big_child.second, u, total);
        mark[u] = 1;
        return u;
    }

    // possible modification: return adjacency list instead of parent list
    vector<int> build_centroid_tree(int s = node_start_id) {
        s = find_centroid(s);
        vector<int> cen_parent(n, -1);
        queue<int> qu;
        for (qu.push(s), cen_parent[s] = s; qu.size(); qu.pop()) {
            int u = qu.front();
            for (auto v: gr[u]) {
                if (mark[v]) continue;
                int cv = find_centroid(v);
                cen_parent[cv] = u;
                qu.push(cv);
            }
        }
        return cen_parent;
    }
};

#define llong long long 
#define len(x) ((int)x.size())
#define rep(i,n) for (int i = -1; ++ i < n; )
#define rep1(i,n) for (int i = 0; i ++ < n; )
#define rand __rand
mt19937 rng(chrono::system_clock::now().time_since_epoch().count());  // or mt19937_64
template<class T = int> T rand(T range = numeric_limits<T>::max()) { return (T)(rng() % range); }

#define CONCAT_(x, y) x##y/*{{{*/
#define CONCAT(x, y) CONCAT_(x, y)
#ifdef LOCAL_DEBUG   
int __db_level = 0;
bool __db_same_line = false;
#define clog cerr << string(!__db_same_line ? __db_level * 2 : 0, ' ')
struct debug_block {
    function<void()> fn;
    void print_name() { __db_same_line = true; fn(); clog << endl; __db_same_line = false;  }
    debug_block(function<void()> fn_): fn(fn_) { clog << "{ "; print_name(); ++__db_level; }
    ~debug_block() { --__db_level; clog << "} "; print_name(); }
};
#define DB(args...) debug_block CONCAT(dbbl, __LINE__)([=]{ clog << args; })
#define deb(...)  if (1) { (clog << "[" #__VA_ARGS__ "] = [" << __VA_ARGS__) << "]"; if (!__db_same_line) clog << endl; }
#else
#define clog if (0) cerr
#define DB(...)
#define deb(...)
#endif
template<class T>
ostream& operator,(ostream& out, const T& thing) { return out << ", " << thing; }
template<class U, class V>
ostream& operator<<(ostream& out, const pair<U, V>& p) { return (out << "(" << p.first, p.second) << ")"; }
template<class A, class B>
ostream& operator<<(ostream& out, const tuple<A, B>& t) { return (out << "(" << get<0>(t), get<1>(t)) << ")"; }
template<class A, class B, class C>
ostream& operator<<(ostream& out, const tuple<A, B, C>& t) { return (out << "(" << get<0>(t), get<1>(t), get<2>(t)) << ")"; }
template<class T> ostream& operator<<(ostream& out, const vector<T>& container) { 
    out << "{";
    if (len(container)) out << container[0];
    rep1(i, len(container) - 1) out, container[i];
    return out << "}";
}
template<class x> vector<typename x::value_type> $v(const x& a) { return vector<typename x::value_type>(a.begin(), a.end()); }
#define ptrtype(x) typename iterator_traits<x>::value_type
template<class u> vector<ptrtype(u)> $v(u a, u b) { return vector<ptrtype(u)>(a, b); }/*}}}*/
// ACTUAL SOLUTION BELOW ////////////////////////////////////////////////////////////

template<int maxbit = 30, class T = int>
struct basis {
    array<T, maxbit> bit;
    int sz;
    basis(): bit{}, sz(0) {
    }
    
    void add(T mask) {
        if (!mask) return ;
        for (int i = maxbit; i--; ) {
            if (!(mask & (1LL << i))) continue;
            if (!bit[i]) {
                bit[i] = mask;
                ++sz;
                return ;
            }
            mask ^= bit[i];
        }
    }
    
    void reset() {
        bit.fill(0);
        sz = 0;
    }
    
    T find_kth(T k, T ans) {
        --k;
        if (k >= (1ll << sz)) return -1;
        int cursz = sz;
        for (int i = maxbit; i--; ) {
            if (!bit[i]) continue;
            --cursz;
            if ((ans >> i) & 1) ans ^= bit[i];
            
            if (k >= (1ll << cursz)) {
                ans ^= bit[i];
                k -= ((T)1 << cursz);
            }
        }
        return ans;
    }
};


const int maxn = 100010;
const int maxq = 202020;
int n, m;

int comp_cost[maxn * 2];
vector<vector<int>> bc_tree;
vector<int> costs[maxn * 2];
centroid_decomposer<vector<vector<int>>> cd(0, bc_tree);

basis<30, int> bb[maxn * 2];
int xor_path[maxn * 2];
void cal_basis(int u, int p = -1) {
    deb(u);
    bb[u].add(comp_cost[u]);
    
    rep(i, len(bc_tree[u])) {
        int v = bc_tree[u][i];
        int cur_cost = costs[u][i];
        if (v == p or cd.mark[v]) continue;
        bb[v] = bb[u];
        xor_path[v] = xor_path[u] ^ cur_cost;
        cal_basis(v, u);
    }
}

struct query {
    int id;
    int u, v, k;
    query(int i, int u_, int v_, int k_)
        : id(i), u(u_), v(v_), k(k_) {}
};

vector<query> queries[maxn * 2];
int ans[maxq];
void process_queries(int where) {
    int cen = cd.find_centroid(where);
    DB(""; deb(cen));
    bb[cen].reset();
    xor_path[cen] = 0;
    cal_basis(cen);
    
    for (auto& qr: queries[cen]) {
        int id = qr.id, u = qr.u, v = qr.v, k = qr.k;
        DB(u, v, k);
        auto bas = bb[u];
        for (int f = 30; f--; ) { 
            bas.add(bb[v].bit[f]); 
        } 
        int path = xor_path[u] ^ xor_path[v];
        deb(path);
        rep(f, 30) deb(f, bas.bit[f]);
        ans[id] = bas.find_kth(k, path);
    }
    
    for (auto v: bc_tree[cen]) {
        if (cd.mark[v]) continue;
        process_queries(v);
    }
}

int main(void) {
    deb(sizeof(bb) / 1024 / 1024);
    ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    cin >> n >> m;
    
    {
        vector<vector<int>> gr(n + 1);
        vector<map<int, int>> temp_cost(2 * n + 1);
        rep(i, m) {
            int u, v, c; cin >> u >> v >> c;
            gr[u].push_back(v);
            gr[v].push_back(u);
            temp_cost[u][v] = temp_cost[v][u] = c;
        }
        
        bc_tree = build_block_tree<BLOCK_CUT_TREE>(n, gr);
        deb(bc_tree);
        // assumsion: my code also ordered the nodes in the bc_tree so that they are
        // in the same order as in the cactus
        for (int comp = n + 1; comp < len(bc_tree); ++comp) {
            auto& cur = bc_tree[comp];
            assert(len(cur) > 1);
            if (len(cur) == 2) {
                int u = cur[0], v = cur[1];
                costs[comp] = {0, temp_cost[u][v]};
                temp_cost[comp][u] = 0;
                temp_cost[comp][v] = costs[comp].back();
                continue;
            }
            
            int prev = cur.back();
            for (auto u: cur) {
                int cur_cost = temp_cost[prev][u];
                comp_cost[comp] ^= cur_cost;
                costs[comp].push_back(comp_cost[comp]);
                temp_cost[comp][u] = comp_cost[comp];
                prev = u;
            }
        }
        rep1(i, n) {
            for (auto comp: bc_tree[i]) {
                costs[i].push_back(temp_cost[comp][i]);
            }
        }
    }
    rep1(i, len(bc_tree) - 1) {
        deb(i, costs[i]);
    }
    
    //  
    cd.reset(len(bc_tree));
    auto cen_tree = cd.build_centroid_tree(1);
    deb(cen_tree);
    
    int q; cin >> q;
    rep(i, q) {
        int u, v, k; cin >> u >> v >> k;
        
        vector<int> par_u = {u}, par_v = {v};
        for (int x = u; cen_tree[x] != x; x = cen_tree[x]) par_u.push_back(cen_tree[x]);
        for (int x = v; cen_tree[x] != x; x = cen_tree[x]) par_v.push_back(cen_tree[x]);
        deb(u, v, par_u, par_v);
        
        int cen_lca = cen_tree[par_u.back()];
        while (len(par_u) and len(par_v) and par_u.back() == par_v.back()) {
            cen_lca = par_u.back();
            par_u.pop_back();
            par_v.pop_back();
        }
        
        queries[cen_lca].emplace_back(i, u, v, k);
    }
    
    cd.reset(len(bc_tree));
    process_queries(1);
    
    rep(i, q) cout << ans[i] << '\n';
    

    return 0;
}

// Remember:
// - Multitest? REFRESHING the data!!!
// - Constrains for each set of data may differs. Should NOT USE the same max constant (maxn)
//   for all of them.
// vim: foldmethod=marker
accuracy: 1
time_ms: 1100
memory_kb: 98824
final_rank: 3

code_snippet: #include <bits/stdc++.h>

#include <utility>

using namespace std;

using int64 = long long;
const int mod = 1e9 + 7;
//const int mod = 998244353;

const int64 infll = (1LL << 62) - 1;
const int inf = (1 << 30) - 1;

struct IoSetup {
  IoSetup() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(10);
    cerr << fixed << setprecision(10);
  }
} iosetup;


template< typename T1, typename T2 >
ostream &operator<<(ostream &os, const pair< T1, T2 > &p) {
  os << p.first << " " << p.second;
  return os;
}

template< typename T1, typename T2 >
istream &operator>>(istream &is, pair< T1, T2 > &p) {
  is >> p.first >> p.second;
  return is;
}

template< typename T >
ostream &operator<<(ostream &os, const vector< T > &v) {
  for(int i = 0; i < (int) v.size(); i++) {
    os << v[i] << (i + 1 != v.size() ? " " : "");
  }
  return os;
}

template< typename T >
istream &operator>>(istream &is, vector< T > &v) {
  for(T &in : v) is >> in;
  return is;
}

template< typename T1, typename T2 >
inline bool chmax(T1 &a, T2 b) { return a < b && (a = b, true); }

template< typename T1, typename T2 >
inline bool chmin(T1 &a, T2 b) { return a > b && (a = b, true); }

template< typename T = int64 >
vector< T > make_v(size_t a) {
  return vector< T >(a);
}

template< typename T, typename... Ts >
auto make_v(size_t a, Ts... ts) {
  return vector< decltype(make_v< T >(ts...)) >(a, make_v< T >(ts...));
}

template< typename T, typename V >
typename enable_if< is_class< T >::value == 0 >::type fill_v(T &t, const V &v) {
  t = v;
}

template< typename T, typename V >
typename enable_if< is_class< T >::value != 0 >::type fill_v(T &t, const V &v) {
  for(auto &e : t) fill_v(e, v);
}

template< typename F >
struct FixPoint : F {
  FixPoint(F &&f) : F(forward< F >(f)) {}

  template< typename... Args >
  decltype(auto) operator()(Args &&... args) const {
    return F::operator()(*this, forward< Args >(args)...);
  }
};

template< typename F >
inline decltype(auto) MFP(F &&f) {
  return FixPoint< F >{forward< F >(f)};
}

template< typename T >
struct Edge {
  int from, to;
  T cost;
  int idx;

  Edge() = default;

  Edge(int from, int to, T cost = 1, int idx = -1) : from(from), to(to), cost(cost), idx(idx) {}

  operator int() const { return to; }
};

template< typename T = int >
struct Graph {
  vector< vector< Edge< T > > > g;
  int es;

  Graph() = default;

  explicit Graph(int n) : g(n), es(0) {}

  size_t size() const {
    return g.size();
  }

  void add_directed_edge(int from, int to, T cost = 1) {
    g[from].emplace_back((Edge< T >) {from, to, cost, es++});
  }

  void add_edge(int from, int to, T cost = 1) {
    g[from].emplace_back((Edge< T >) {from, to, cost, es});
    g[to].emplace_back((Edge< T >) {to, from, cost, es++});
  }

  void read(int M, int padding = -1, bool weighted = false, bool directed = false) {
    for(int i = 0; i < M; i++) {
      int a, b;
      cin >> a >> b;
      a += padding;
      b += padding;
      T c = T(1);
      if(weighted) cin >> c;
      if(directed) add_directed_edge(a, b, c);
      else add_edge(a, b, c);
    }
  }
};

template< typename T = int >
struct LowLink : Graph< T > {
public:
  using Graph< T >::Graph;
  vector< int > ord, low, articulation;
  vector< Edge< T > > bridge;
  using Graph< T >::g;

  virtual void build() {
    used.assign(g.size(), 0);
    ord.assign(g.size(), 0);
    low.assign(g.size(), 0);
    int k = 0;
    for(int i = 0; i < (int) g.size(); i++) {
      if(!used[i]) k = dfs(i, k, -1);
    }
  }

  explicit LowLink(const Graph< T > &g) : Graph< T >(g) {}

private:
  vector< int > used;

  int dfs(int idx, int k, int par) {
    used[idx] = true;
    ord[idx] = k++;
    low[idx] = ord[idx];
    bool is_articulation = false, beet = false;
    int cnt = 0;
    for(auto &to : g[idx]) {
      if(to == par && !exchange(beet, true)) {
        continue;
      }
      if(!used[to]) {
        ++cnt;
        k = dfs(to, k, idx);
        low[idx] = min(low[idx], low[to]);
        is_articulation |= par >= 0 && low[to] >= ord[idx];
        if(ord[idx] < low[to]) bridge.emplace_back(to);
      } else {
        low[idx] = min(low[idx], ord[to]);
      }
    }
    is_articulation |= par == -1 && cnt > 1;
    if(is_articulation) articulation.push_back(idx);
    return k;
  }
};

template< typename T = int >
struct BiConnectedComponents : LowLink< T > {
public:
  using LowLink< T >::LowLink;
  using LowLink< T >::g;
  using LowLink< T >::ord;
  using LowLink< T >::low;

  vector< vector< Edge< T > > > bc;

  void build() override {
    LowLink< T >::build();
    used.assign(g.size(), 0);
    for(int i = 0; i < used.size(); i++) {
      if(!used[i]) dfs(i, -1);
    }
  }

  explicit BiConnectedComponents(const Graph< T > &g) : Graph< T >(g) {}

private:
  vector< int > used;
  vector< Edge< T > > tmp;

  void dfs(int idx, int par) {
    used[idx] = true;
    bool beet = false;
    for(auto &to : g[idx]) {
      if(to == par && !exchange(beet, true)) continue;
      if(!used[to] || ord[to] < ord[idx]) {
        tmp.emplace_back(to);
      }
      if(!used[to]) {
        dfs(to, idx);
        if(low[to] >= ord[idx]) {
          bc.emplace_back();
          for(;;) {
            auto e = tmp.back();
            bc.back().emplace_back(e);
            tmp.pop_back();
            if(e.idx == to.idx) break;
          }
        }
      }
    }
  }
};

template< typename T = int >
struct BlockCutTree : BiConnectedComponents< T > {
public:
  using BiConnectedComponents< T >::BiConnectedComponents;
  using BiConnectedComponents< T >::g;
  using BiConnectedComponents< T >::articulation;
  using BiConnectedComponents< T >::bc;

  vector< int > rev;
  vector< vector< int > > group;
  Graph< T > tree;

  explicit BlockCutTree(const Graph< T > &g) : Graph< T >(g) {}

  int operator[](const int &k) const {
    return rev[k];
  }

  void build() override {
    BiConnectedComponents< T >::build();
    rev.assign(g.size(), -1);
    int ptr = (int) bc.size();
    for(auto &idx : articulation) {
      rev[idx] = ptr++;
    }
    vector< int > last(ptr, -1);
    tree = Graph< T >(ptr);
    for(int i = 0; i < (int) bc.size(); i++) {
      for(auto &e : bc[i]) {
        for(auto &ver : {e.from, e.to}) {
          if(rev[ver] >= (int) bc.size()) {
            if(exchange(last[rev[ver]], i) != i) {
              tree.add_edge(rev[ver], i, e.cost);
            }
          } else {
            rev[ver] = i;
          }
        }
      }
    }
    group.resize(ptr);
    for(int i = 0; i < (int) g.size(); i++) {
      group[rev[i]].emplace_back(i);
    }
  }
};

struct UnionFind {
  vector< int > data;

  explicit UnionFind(int sz) {
    data.assign(sz, -1);
  }

  bool unite(int x, int y) {
    x = find(x), y = find(y);
    if(x == y) return (false);
    if(data[x] > data[y]) swap(x, y);
    data[x] += data[y];
    data[y] = x;
    return (true);
  }

  int find(int k) {
    if(data[k] < 0) return (k);
    return (data[k] = find(data[k]));
  }

  int size(int k) {
    return (-data[find(k)]);
  }
};


template< typename T >
struct CentroidDecomposition : Graph< T > {
public:
  using Graph< T >::Graph;
  using Graph< T >::g;
  Graph< int > tree;

  int build(int t = 0) {
    sub.assign(g.size(), 0);
    v.assign(g.size(), 0);
    tree = Graph< T >(g.size());
    return build_dfs(0);
  }

  explicit CentroidDecomposition(const Graph< T > &g) : Graph< T >(g) {}

private:
  vector< int > sub;
  vector< int > v;

  inline int build_dfs(int idx, int par) {
    sub[idx] = 1;
    for(auto &to : g[idx]) {
      if(to == par || v[to]) continue;
      sub[idx] += build_dfs(to, idx);
    }
    return sub[idx];
  }

  inline int search_centroid(int idx, int par, const int mid) {
    for(auto &to : g[idx]) {
      if(to == par || v[to]) continue;
      if(sub[to] > mid) return search_centroid(to, idx, mid);
    }
    return idx;
  }

  inline int build_dfs(int idx) {
    int centroid = search_centroid(idx, -1, build_dfs(idx, -1) / 2);
    v[centroid] = true;
    for(auto &to : g[centroid]) {
      if(!v[to]) tree.add_directed_edge(centroid, build_dfs(to));
    }
    v[centroid] = false;
    return centroid;
  }
};


int main() {
  int N, M;
  cin >> N >> M;
  BlockCutTree< int > g(N);
  Graph<> h(N);
  UnionFind uf(N);
  vector< int > A(M), B(M), C(M);
  for(int i = 0; i < M; i++) {
    cin >> A[i] >> B[i] >> C[i];
    --A[i], --B[i];
    g.add_edge(A[i], B[i], C[i]);
    if(uf.unite(A[i], B[i])) h.add_edge(A[i], B[i], C[i]);
  }
  g.build();

  vector< int > sum(N);
  MFP([&](auto rec, int idx, int par) -> void {
    for(auto &to : h.g[idx]) {
      if(to != par) {
        sum[to] = sum[idx] ^ to.cost;
        rec(to, idx);
      }
    }
  })(0, -1);

  auto &t = g.tree;
  vector< int > weight(t.size());

  Graph< int > tree(t);
  CentroidDecomposition< int > cpd(tree);
  int root = cpd.build();
  auto &ushitapunichia = cpd.tree;


  {
    for(int i = 0; i < g.bc.size(); i++) {
      for(auto &e : g.bc[i]) weight[i] ^= e.cost;
    }
    for(int i = 0; i < t.size(); i++) {
      if(i < g.bc.size() && g.bc[i].size() >= 2) continue;
      weight[i] = 0;
    }
  }

  using vi = vector< int >;

  auto f = [](vi &a, int b) {
    for(int y : a) chmin(b, b ^ y);
    if(b) a.emplace_back(b);
  };

  int Q;
  cin >> Q;
  vector< int > X(Q), Y(Q), K(Q), Z(Q);
  for(int i = 0; i < Q; i++) {
    cin >> X[i] >> Y[i] >> K[i];
    --X[i], --Y[i], --K[i];
    Z[i] = sum[X[i]] ^ sum[Y[i]];
    X[i] = g[X[i]];
    Y[i] = g[Y[i]];
  }

  vector< vector< int > > ev(t.size());
  for(int i = 0; i < Q; i++) {
    ev[X[i]].emplace_back(i);
    ev[Y[i]].emplace_back(i);
  }
  vector< int > used(t.size());
  vector< vector< int > > cash(t.size());
  vector< int > last(Q);
  int ptr = 1;
  vector< int > ans(Q);


  auto calc_ans = [&](const vector< int > &a, vector< int > b, int k, int base) {
    for(int x : a) {
      if(b.size() >= 30) break;
      for(int y : b) chmin(x, x ^ y);
      if(x) b.emplace_back(x);
    }
    auto &tap = b;
    if(1 << tap.size() <= k) {
      return -1;
    } else {
      sort(tap.begin(), tap.end());
      for(int j = (int) tap.size() - 1; j >= 0; j--) {
        if(k < (1 << j)) {
          chmin(base, base ^ tap[j]);
        } else {
          k -= 1 << j;
          chmax(base, base ^ tap[j]);
        }
      }
      return base;
    }
  };

  auto add_dfs = MFP([&](auto add_dfs, int idx, int par, vector< int > base, int Left, int id) -> void {
    if(weight[idx]) f(base, weight[idx]);
    cash[idx] = base;

    for(auto &q : ev[idx]) {
      if(Left <= last[q] && last[q] < id) ans[q] = calc_ans(cash[X[q]], cash[Y[q]], K[q], Z[q]);
      last[q] = id;
    }

    for(auto &to : t.g[idx]) {
      if(to == par) continue;
      if(used[to]) continue;
      add_dfs(to, idx, base, Left, id);
    }
  });


  MFP([&](auto dfs, int centroid) -> void {
    used[centroid] = true;

    vector< int > base;
    int Left = ptr;
    if(weight[centroid]) base.emplace_back(weight[centroid]);
    for(auto &q : ev[centroid]) {
      if(last[q] == ptr) ans[q] = calc_ans(base, base, K[q], Z[q]);
      last[q] = ptr;
    }
    cash[centroid] = base;
    ++ptr;

    for(auto &to : t.g[centroid]) {
      if(used[to]) continue;
      add_dfs(to, centroid, base, Left, ptr++);
    }

    for(auto &to : ushitapunichia.g[centroid]) {
      dfs(to);
    }
    used[centroid] = false;
  })(root);


  for(auto &p : ans) cout << p << "\n";
}
accuracy: 1
time_ms: 790
memory_kb: 142428
final_rank: 5

code_snippet: #include <bits/stdc++.h>

#include <utility>

using namespace std;

using int64 = long long;
const int mod = 1e9 + 7;
//const int mod = 998244353;

const int64 infll = (1LL << 62) - 1;
const int inf = (1 << 30) - 1;

struct IoSetup {
  IoSetup() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(10);
    cerr << fixed << setprecision(10);
  }
} iosetup;


template< typename T1, typename T2 >
ostream &operator<<(ostream &os, const pair< T1, T2 > &p) {
  os << p.first << " " << p.second;
  return os;
}

template< typename T1, typename T2 >
istream &operator>>(istream &is, pair< T1, T2 > &p) {
  is >> p.first >> p.second;
  return is;
}

template< typename T >
ostream &operator<<(ostream &os, const vector< T > &v) {
  for(int i = 0; i < (int) v.size(); i++) {
    os << v[i] << (i + 1 != v.size() ? " " : "");
  }
  return os;
}

template< typename T >
istream &operator>>(istream &is, vector< T > &v) {
  for(T &in : v) is >> in;
  return is;
}

template< typename T1, typename T2 >
inline bool chmax(T1 &a, T2 b) { return a < b && (a = b, true); }

template< typename T1, typename T2 >
inline bool chmin(T1 &a, T2 b) { return a > b && (a = b, true); }

template< typename T = int64 >
vector< T > make_v(size_t a) {
  return vector< T >(a);
}

template< typename T, typename... Ts >
auto make_v(size_t a, Ts... ts) {
  return vector< decltype(make_v< T >(ts...)) >(a, make_v< T >(ts...));
}

template< typename T, typename V >
typename enable_if< is_class< T >::value == 0 >::type fill_v(T &t, const V &v) {
  t = v;
}

template< typename T, typename V >
typename enable_if< is_class< T >::value != 0 >::type fill_v(T &t, const V &v) {
  for(auto &e : t) fill_v(e, v);
}

template< typename F >
struct FixPoint : F {
  FixPoint(F &&f) : F(forward< F >(f)) {}

  template< typename... Args >
  decltype(auto) operator()(Args &&... args) const {
    return F::operator()(*this, forward< Args >(args)...);
  }
};

template< typename F >
inline decltype(auto) MFP(F &&f) {
  return FixPoint< F >{forward< F >(f)};
}

template< typename T >
struct Edge {
  int from, to;
  T cost;
  int idx;

  Edge() = default;

  Edge(int from, int to, T cost = 1, int idx = -1) : from(from), to(to), cost(cost), idx(idx) {}

  operator int() const { return to; }
};

template< typename T = int >
struct Graph {
  vector< vector< Edge< T > > > g;
  int es;

  Graph() = default;

  explicit Graph(int n) : g(n), es(0) {}

  size_t size() const {
    return g.size();
  }

  void add_directed_edge(int from, int to, T cost = 1) {
    g[from].emplace_back((Edge< T >) {from, to, cost, es++});
  }

  void add_edge(int from, int to, T cost = 1) {
    g[from].emplace_back((Edge< T >) {from, to, cost, es});
    g[to].emplace_back((Edge< T >) {to, from, cost, es++});
  }

  void read(int M, int padding = -1, bool weighted = false, bool directed = false) {
    for(int i = 0; i < M; i++) {
      int a, b;
      cin >> a >> b;
      a += padding;
      b += padding;
      T c = T(1);
      if(weighted) cin >> c;
      if(directed) add_directed_edge(a, b, c);
      else add_edge(a, b, c);
    }
  }
};

template< typename T = int >
struct LowLink : Graph< T > {
public:
  using Graph< T >::Graph;
  vector< int > ord, low, articulation;
  vector< Edge< T > > bridge;
  using Graph< T >::g;

  virtual void build() {
    used.assign(g.size(), 0);
    ord.assign(g.size(), 0);
    low.assign(g.size(), 0);
    int k = 0;
    for(int i = 0; i < (int) g.size(); i++) {
      if(!used[i]) k = dfs(i, k, -1);
    }
  }

  explicit LowLink(const Graph< T > &g) : Graph< T >(g) {}

private:
  vector< int > used;

  int dfs(int idx, int k, int par) {
    used[idx] = true;
    ord[idx] = k++;
    low[idx] = ord[idx];
    bool is_articulation = false, beet = false;
    int cnt = 0;
    for(auto &to : g[idx]) {
      if(to == par && !exchange(beet, true)) {
        continue;
      }
      if(!used[to]) {
        ++cnt;
        k = dfs(to, k, idx);
        low[idx] = min(low[idx], low[to]);
        is_articulation |= par >= 0 && low[to] >= ord[idx];
        if(ord[idx] < low[to]) bridge.emplace_back(to);
      } else {
        low[idx] = min(low[idx], ord[to]);
      }
    }
    is_articulation |= par == -1 && cnt > 1;
    if(is_articulation) articulation.push_back(idx);
    return k;
  }
};

template< typename T = int >
struct BiConnectedComponents : LowLink< T > {
public:
  using LowLink< T >::LowLink;
  using LowLink< T >::g;
  using LowLink< T >::ord;
  using LowLink< T >::low;

  vector< vector< Edge< T > > > bc;

  void build() override {
    LowLink< T >::build();
    used.assign(g.size(), 0);
    for(int i = 0; i < used.size(); i++) {
      if(!used[i]) dfs(i, -1);
    }
  }

  explicit BiConnectedComponents(const Graph< T > &g) : Graph< T >(g) {}

private:
  vector< int > used;
  vector< Edge< T > > tmp;

  void dfs(int idx, int par) {
    used[idx] = true;
    bool beet = false;
    for(auto &to : g[idx]) {
      if(to == par && !exchange(beet, true)) continue;
      if(!used[to] || ord[to] < ord[idx]) {
        tmp.emplace_back(to);
      }
      if(!used[to]) {
        dfs(to, idx);
        if(low[to] >= ord[idx]) {
          bc.emplace_back();
          for(;;) {
            auto e = tmp.back();
            bc.back().emplace_back(e);
            tmp.pop_back();
            if(e.idx == to.idx) break;
          }
        }
      }
    }
  }
};

template< typename T = int >
struct BlockCutTree : BiConnectedComponents< T > {
public:
  using BiConnectedComponents< T >::BiConnectedComponents;
  using BiConnectedComponents< T >::g;
  using BiConnectedComponents< T >::articulation;
  using BiConnectedComponents< T >::bc;

  vector< int > rev;
  vector< vector< int > > group;
  Graph< T > tree;

  explicit BlockCutTree(const Graph< T > &g) : Graph< T >(g) {}

  int operator[](const int &k) const {
    return rev[k];
  }

  void build() override {
    BiConnectedComponents< T >::build();
    rev.assign(g.size(), -1);
    int ptr = (int) bc.size();
    for(auto &idx : articulation) {
      rev[idx] = ptr++;
    }
    vector< int > last(ptr, -1);
    tree = Graph< T >(ptr);
    for(int i = 0; i < (int) bc.size(); i++) {
      for(auto &e : bc[i]) {
        for(auto &ver : {e.from, e.to}) {
          if(rev[ver] >= (int) bc.size()) {
            if(exchange(last[rev[ver]], i) != i) {
              tree.add_edge(rev[ver], i, e.cost);
            }
          } else {
            rev[ver] = i;
          }
        }
      }
    }
    group.resize(ptr);
    for(int i = 0; i < (int) g.size(); i++) {
      group[rev[i]].emplace_back(i);
    }
  }
};

struct UnionFind {
  vector< int > data;

  explicit UnionFind(int sz) {
    data.assign(sz, -1);
  }

  bool unite(int x, int y) {
    x = find(x), y = find(y);
    if(x == y) return (false);
    if(data[x] > data[y]) swap(x, y);
    data[x] += data[y];
    data[y] = x;
    return (true);
  }

  int find(int k) {
    if(data[k] < 0) return (k);
    return (data[k] = find(data[k]));
  }

  int size(int k) {
    return (-data[find(k)]);
  }
};


template< typename T >
struct CentroidDecomposition : Graph< T > {
public:
  using Graph< T >::Graph;
  using Graph< T >::g;
  Graph< int > tree;

  int build(int t = 0) {
    sub.assign(g.size(), 0);
    v.assign(g.size(), 0);
    belong.assign(g.size(), vector< int >());
    tree = Graph< T >(g.size());
    return build_dfs(0);
  }

  explicit CentroidDecomposition(const Graph< T > &g) : Graph< T >(g) {}

private:
  vector< int > sub;
  vector< vector< int > > belong;
  vector< int > v;

  inline int build_dfs(int idx, int par) {
    sub[idx] = 1;
    for(auto &to : g[idx]) {
      if(to == par || v[to]) continue;
      sub[idx] += build_dfs(to, idx);
    }
    return sub[idx];
  }

  inline int search_centroid(int idx, int par, const int mid) {
    for(auto &to : g[idx]) {
      if(to == par || v[to]) continue;
      if(sub[to] > mid) return search_centroid(to, idx, mid);
    }
    return idx;
  }

  inline void belong_dfs(int idx, int par, int centroid) {
    belong[idx].emplace_back(centroid);
    for(auto &to : g[idx]) {
      if(to == par || v[to]) continue;
      belong_dfs(to, idx, centroid);
    }
  }

  inline int build_dfs(int idx) {
    int centroid = search_centroid(idx, -1, build_dfs(idx, -1) / 2);
    v[centroid] = true;
    belong_dfs(centroid, -1, centroid);
    for(auto &to : g[centroid]) {
      if(!v[to]) tree.add_directed_edge(centroid, build_dfs(to));
    }
    v[centroid] = false;
    return centroid;
  }
};


int main() {
  int N, M;
  cin >> N >> M;
  BlockCutTree< int > g(N);
  Graph<> h(N);
  UnionFind uf(N);
  vector< int > A(M), B(M), C(M);
  for(int i = 0; i < M; i++) {
    cin >> A[i] >> B[i] >> C[i];
    --A[i], --B[i];
    g.add_edge(A[i], B[i], C[i]);
    if(uf.unite(A[i], B[i])) h.add_edge(A[i], B[i], C[i]);
  }
  g.build();

  vector< int > sum(N);
  MFP([&](auto rec, int idx, int par) -> void {
    for(auto &to : h.g[idx]) {
      if(to != par) {
        sum[to] = sum[idx] ^ to.cost;
        rec(to, idx);
      }
    }
  })(0, -1);

  auto &t = g.tree;
  vector< int > weight(t.size());

  Graph< int > tree(t);
  CentroidDecomposition< int > cpd(tree);
  int root = cpd.build();
  auto &ushitapunichia = cpd.tree;


  {
    for(int i = 0; i < g.bc.size(); i++) {
      for(auto &e : g.bc[i]) weight[i] ^= e.cost;
    }
    for(int i = 0; i < t.size(); i++) {
      if(i < g.bc.size() && g.bc[i].size() >= 2) continue;
      weight[i] = 0;
    }
  }

  using vi = vector< int >;

  auto f = [](vi &a, int b) {
    for(int y : a) chmin(b, b ^ y);
    if(b) a.emplace_back(b);
  };

  int Q;
  cin >> Q;
  vector< int > X(Q), Y(Q), K(Q), Z(Q);
  for(int i = 0; i < Q; i++) {
    cin >> X[i] >> Y[i] >> K[i];
    --X[i], --Y[i], --K[i];
    Z[i] = sum[X[i]] ^ sum[Y[i]];
    X[i] = g[X[i]];
    Y[i] = g[Y[i]];
  }

  vector< vector< int > > ev(t.size());
  for(int i = 0; i < Q; i++) {
    ev[X[i]].emplace_back(i);
    ev[Y[i]].emplace_back(i);
  }
  vector< int > used(t.size());
  vector< vector< int > > cash(t.size());
  vector< int > last(Q);
  int ptr = 1;
  vector< int > ans(Q);


  auto calc_ans = [&](const vector< int > &a, vector< int > b, int k, int base) {
    for(int x : a) {
      if(b.size() >= 30) break;
      for(int y : b) chmin(x, x ^ y);
      if(x) b.emplace_back(x);
    }
    auto &tap = b;
    if(1 << tap.size() <= k) {
      return -1;
    } else {
      sort(tap.begin(), tap.end());
      for(int j = (int) tap.size() - 1; j >= 0; j--) {
        if(k < (1 << j)) {
          chmin(base, base ^ tap[j]);
        } else {
          k -= 1 << j;
          chmax(base, base ^ tap[j]);
        }
      }
      return base;
    }
  };

  auto add_dfs = MFP([&](auto add_dfs, int idx, int par, vector< int > base, int Left, int id) -> void {
    if(weight[idx]) f(base, weight[idx]);
    cash[idx] = base;

    for(auto &q : ev[idx]) {
      if(Left <= last[q] && last[q] < id) ans[q] = calc_ans(cash[X[q]], cash[Y[q]], K[q], Z[q]);
      last[q] = id;
    }

    for(auto &to : t.g[idx]) {
      if(to == par) continue;
      if(used[to]) continue;
      add_dfs(to, idx, base, Left, id);
    }
  });


  MFP([&](auto dfs, int centroid) -> void {
    used[centroid] = true;

    vector< int > base;
    int Left = ptr;
    if(weight[centroid]) base.emplace_back(weight[centroid]);
    for(auto &q : ev[centroid]) {
      if(last[q] == ptr) ans[q] = calc_ans(base, base, K[q], Z[q]);
      last[q] = ptr;
    }
    cash[centroid] = base;
    ++ptr;

    for(auto &to : t.g[centroid]) {
      if(used[to]) continue;
      add_dfs(to, centroid, base, Left, ptr++);
    }

    for(auto &to : ushitapunichia.g[centroid]) {
      dfs(to);
    }
    used[centroid] = false;
  })(root);


  for(auto &p : ans) cout << p << "\n";
}
accuracy: 1
time_ms: 840
memory_kb: 171420
final_rank: 7

code_snippet: #include <bits/stdc++.h>

using namespace std;

using int64 = long long;
const int mod = 1e9 + 7;
//const int mod = 998244353;

const int64 infll = (1LL << 62) - 1;
const int inf = (1 << 30) - 1;

struct IoSetup {
  IoSetup() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(10);
    cerr << fixed << setprecision(10);
  }
} iosetup;


template< typename T1, typename T2 >
ostream &operator<<(ostream &os, const pair< T1, T2 > &p) {
  os << p.first << " " << p.second;
  return os;
}

template< typename T1, typename T2 >
istream &operator>>(istream &is, pair< T1, T2 > &p) {
  is >> p.first >> p.second;
  return is;
}

template< typename T >
ostream &operator<<(ostream &os, const vector< T > &v) {
  for(int i = 0; i < (int) v.size(); i++) {
    os << v[i] << (i + 1 != v.size() ? " " : "");
  }
  return os;
}

template< typename T >
istream &operator>>(istream &is, vector< T > &v) {
  for(T &in : v) is >> in;
  return is;
}

template< typename T1, typename T2 >
inline bool chmax(T1 &a, T2 b) { return a < b && (a = b, true); }

template< typename T1, typename T2 >
inline bool chmin(T1 &a, T2 b) { return a > b && (a = b, true); }

template< typename T = int64 >
vector< T > make_v(size_t a) {
  return vector< T >(a);
}

template< typename T, typename... Ts >
auto make_v(size_t a, Ts... ts) {
  return vector< decltype(make_v< T >(ts...)) >(a, make_v< T >(ts...));
}

template< typename T, typename V >
typename enable_if< is_class< T >::value == 0 >::type fill_v(T &t, const V &v) {
  t = v;
}

template< typename T, typename V >
typename enable_if< is_class< T >::value != 0 >::type fill_v(T &t, const V &v) {
  for(auto &e : t) fill_v(e, v);
}

template< typename F >
struct FixPoint : F {
  FixPoint(F &&f) : F(forward< F >(f)) {}

  template< typename... Args >
  decltype(auto) operator()(Args &&... args) const {
    return F::operator()(*this, forward< Args >(args)...);
  }
};

template< typename F >
inline decltype(auto) MFP(F &&f) {
  return FixPoint< F >{forward< F >(f)};
}

template< typename T >
struct Edge {
  int from, to;
  T cost;
  int idx;

  Edge(int from, int to, T cost = 1, int idx = -1) : from(from), to(to), cost(cost), idx(idx) {}

  operator int() const { return to; }
};

template< typename T = int >
struct Graph {
  vector< vector< Edge< T > > > g;
  vector< Edge< T > > edges;
  int esz;

  Graph() = default;

  Graph(int n) : g(n), esz(0) {}

  size_t size() const {
    return g.size();
  }

  void add_directed_edge(int from, int to, T cost = 1, int idx = -1) {
    if(idx == -1) idx = esz++;
    g[from].emplace_back(from, to, cost, idx);
    edges.emplace_back(from, to, cost, idx);
  }

  void add_edge(int from, int to, T cost = 1, int idx = -1) {
    if(idx == -1) idx = esz++;
    g[from].emplace_back(from, to, cost, idx);
    edges.emplace_back(from, to, cost, idx);
    g[to].emplace_back(to, from, cost, idx);
  }

  void read(int M, int padding = -1, bool weighted = false, bool directed = false) {
    for(int i = 0; i < M; i++) {
      int a, b;
      cin >> a >> b;
      a += padding;
      b += padding;
      T c = T(1);
      if(weighted) cin >> c;
      if(directed) add_edge_directed(a, b, c);
      else add_edge(a, b, c);
    }
  }
};

template< typename T = int >
struct LowLink : Graph< T > {
public:
  using Graph< T >::Graph;
  vector< int > ord, low, articulation, bridge;
  using Graph< T >::g;

  virtual void build() {
    used.assign(g.size(), 0);
    ord.assign(g.size(), 0);
    low.assign(g.size(), 0);
    int k = 0;
    for(int i = 0; i < (int) g.size(); i++) {
      if(!used[i]) k = dfs(i, k, -1);
    }
  }

  explicit LowLink(const Graph< T > &g) : Graph< T >(g) {}

private:
  vector< int > used;

  int dfs(int idx, int k, int par) {
    used[idx] = true;
    ord[idx] = k++;
    low[idx] = ord[idx];
    bool is_articulation = false, beet = false;
    int cnt = 0;
    for(auto &to : g[idx]) {
      if(to == par && !exchange(beet, true)) {
        continue;
      }
      if(!used[to]) {
        ++cnt;
        k = dfs(to, k, idx);
        low[idx] = min(low[idx], low[to]);
        is_articulation |= par >= 0 && low[to] >= ord[idx];
        if(ord[idx] < low[to]) bridge.emplace_back(to.idx);
      } else {
        low[idx] = min(low[idx], ord[to]);
      }
    }
    is_articulation |= par == -1 && cnt > 1;
    if(is_articulation) articulation.push_back(idx);
    return k;
  }
};

template< typename T = int >
struct BiConnectedComponents : LowLink< T > {
public:
  using LowLink< T >::LowLink;
  using LowLink< T >::g;
  using LowLink< T >::edges;
  using LowLink< T >::ord;
  using LowLink< T >::low;

  vector< vector< int > > bc;

  void build() override {
    LowLink< T >::build();
    used.assign(g.size(), 0);
    for(int i = 0; i < used.size(); i++) {
      if(!used[i]) dfs(i, -1);
    }
  }

  explicit BiConnectedComponents(const Graph< T > &g) : Graph< T >(g) {}

private:
  vector< int > used, tmp;

  void dfs(int idx, int par) {
    used[idx] = true;
    bool beet = false;
    for(auto &to : g[idx]) {
      if(to == par && !exchange(beet, true)) continue;
      if(!used[to] || ord[to] < ord[idx]) {
        tmp.emplace_back(to.idx);
      }
      if(!used[to]) {
        dfs(to, idx);
        if(low[to] >= ord[idx]) {
          bc.emplace_back();
          for(;;) {
            auto e = tmp.back();
            bc.back().emplace_back(e);
            tmp.pop_back();
            if(e == to.idx) break;
          }
        }
      }
    }
  }
};

template< typename T = int >
struct BlockCutTree : BiConnectedComponents< T > {
public:
  using BiConnectedComponents< T >::BiConnectedComponents;
  using BiConnectedComponents< T >::g;
  using BiConnectedComponents< T >::edges;
  using BiConnectedComponents< T >::articulation;
  using BiConnectedComponents< T >::bc;

  vector< int > rev;
  vector< vector< int > > group;
  Graph< T > tree;

  explicit BlockCutTree(const Graph< T > &g) : Graph< T >(g) {}

  int operator[](const int &k) const {
    return rev[k];
  }

  void build() override {
    BiConnectedComponents< T >::build();
    rev.assign(g.size(), -1);
    int ptr = (int) bc.size();
    for(auto &idx : articulation) {
      rev[idx] = ptr++;
    }
    vector< int > last(ptr, -1);
    tree = Graph< T >(ptr);
    for(int i = 0; i < (int) bc.size(); i++) {
      for(auto &id : bc[i]) {
        for(auto ver : {edges[id].from, edges[id].to}) {
          if(rev[ver] >= (int) bc.size()) {
            if(exchange(last[rev[ver]], i) != i) {
              tree.add_edge(rev[ver], i, edges[id].cost);
            }
          } else {
            rev[ver] = i;
          }
        }
      }
    }
    group.resize(ptr);
    for(int i = 0; i < (int) g.size(); i++) {
      group[rev[i]].emplace_back(i);
    }
  }
};

struct UnionFind {
  vector< int > data;

  UnionFind(int sz) {
    data.assign(sz, -1);
  }

  bool unite(int x, int y) {
    x = find(x), y = find(y);
    if(x == y) return (false);
    if(data[x] > data[y]) swap(x, y);
    data[x] += data[y];
    data[y] = x;
    return (true);
  }

  int find(int k) {
    if(data[k] < 0) return (k);
    return (data[k] = find(data[k]));
  }

  int size(int k) {
    return (-data[find(k)]);
  }
};


template< typename T >
struct DoublingLowestCommonAncestor : Graph< T > {
public:
  using F = function< T(T, T) >;
  using Graph< T >::edges;
  using Graph< T >::g;
  vector< int > dep;
  vector< T > sum;
  vector< vector< int > > table;
  const int LOG;
  const F f;

  explicit DoublingLowestCommonAncestor(int n, const F &f = plus< T >())
      : LOG(32 - __builtin_clz(g.size())), Graph< T >(n), f(f) {}

  explicit DoublingLowestCommonAncestor(const Graph< T > &g, const F &f = plus< T >())
      : LOG(32 - __builtin_clz(g.size())), Graph< T >(g), f(f) {}

  void build() {
    dep.assign(g.size(), 0);
    sum.assign(g.size(), 0);
    table.assign(LOG, vector< int >(g.size(), -1));
    dfs(0, -1, 0);
    for(int k = 0; k + 1 < LOG; k++) {
      for(int i = 0; i < table[k].size(); i++) {
        if(table[k][i] == -1) table[k + 1][i] = -1;
        else table[k + 1][i] = table[k][table[k][i]];
      }
    }
  }

  int lca(int u, int v) {
    if(dep[u] > dep[v]) swap(u, v);
    for(int i = LOG - 1; i >= 0; i--) {
      if(((dep[v] - dep[u]) >> i) & 1) v = table[i][v];
    }
    if(u == v) return u;
    for(int i = LOG - 1; i >= 0; i--) {
      if(table[i][u] != table[i][v]) {
        u = table[i][u];
        v = table[i][v];
      }
    }
    return table[0][u];
  }

private:
  void dfs(int idx, int par, int d) {
    table[0][idx] = par;
    dep[idx] = d;
    for(auto &to : g[idx]) {
      if(to != par) {
        sum[to] = f(sum[idx], to.cost);
        dfs(to, idx, d + 1);
      }
    }
  }
};


template< typename T >
struct CentroidDecomposition : Graph< T > {
public:
  using Graph< T >::Graph;
  using Graph< T >::edges;
  using Graph< T >::g;
  Graph< int > tree;

  int build(int t = 0) {
    sub.assign(g.size(), 0);
    v.assign(g.size(), 0);
    belong.assign(g.size(), vector< int >());
    tree = Graph< T >(g.size());
    return build_dfs(0);
  }

  explicit CentroidDecomposition(const Graph< T > &g) : Graph< T >(g) {}

private:
  vector< int > sub;
  vector< vector< int > > belong;
  vector< int > v;

  inline int build_dfs(int idx, int par) {
    sub[idx] = 1;
    for(auto &to : g[idx]) {
      if(to == par || v[to]) continue;
      sub[idx] += build_dfs(to, idx);
    }
    return sub[idx];
  }

  inline int search_centroid(int idx, int par, const int mid) {
    for(auto &to : g[idx]) {
      if(to == par || v[to]) continue;
      if(sub[to] > mid) return search_centroid(to, idx, mid);
    }
    return idx;
  }

  inline void belong_dfs(int idx, int par, int centroid) {
    belong[idx].emplace_back(centroid);
    for(auto &to : g[idx]) {
      if(to == par || v[to]) continue;
      belong_dfs(to, idx, centroid);
    }
  }

  inline int build_dfs(int idx) {
    int centroid = search_centroid(idx, -1, build_dfs(idx, -1) / 2);
    v[centroid] = true;
    belong_dfs(centroid, -1, centroid);
    for(auto &to : g[centroid]) {
      if(!v[to]) tree.add_directed_edge(centroid, build_dfs(to));
    }
    v[centroid] = false;
    return centroid;
  }
};


int main() {
  int N, M;
  cin >> N >> M;
  BlockCutTree< int > g(N);
  DoublingLowestCommonAncestor< int > h(N, [](int a, int b) { return a ^ b; });
  UnionFind uf(N);
  vector< int > A(M), B(M), C(M);
  for(int i = 0; i < M; i++) {
    cin >> A[i] >> B[i] >> C[i];
    --A[i], --B[i];
    g.add_edge(A[i], B[i], C[i]);
    if(uf.unite(A[i], B[i])) h.add_edge(A[i], B[i], C[i]);
  }
  g.build();
  h.build();

  auto &t = g.tree;
  vector< int > weight(t.size());

  Graph< int > tree(t);
  CentroidDecomposition< int > cpd(tree);
  int root = cpd.build();
  auto &ushitapunichia = cpd.tree;


  {
    for(int i = 0; i < g.bc.size(); i++) {
      for(auto &id : g.bc[i]) weight[i] ^= g.edges[id].cost;
    }
    for(int i = 0; i < t.size(); i++) {
      if(i < g.bc.size() && g.bc[i].size() >= 2) continue;
      weight[i] = 0;
    }
  }

  using vi = vector< int >;

  auto f = [](vi &a, int b) {
    for(int y : a) chmin(b, b ^ y);
    if(b) a.emplace_back(b);
  };

  int Q;
  cin >> Q;
  vector< int > X(Q), Y(Q), K(Q), Z(Q);
  for(int i = 0; i < Q; i++) {
    cin >> X[i] >> Y[i] >> K[i];
    --X[i], --Y[i], --K[i];
    Z[i] = h.sum[X[i]] ^ h.sum[Y[i]];
    X[i] = g[X[i]];
    Y[i] = g[Y[i]];
  }

  vector< vector< int > > ev(t.size());
  for(int i = 0; i < Q; i++) {
    ev[X[i]].emplace_back(i);
    ev[Y[i]].emplace_back(i);
  }
  vector< int > used(t.size());
  vector< vector< int > > cash(t.size());
  vector< int > last(Q);
  int ptr = 1;
  vector< int > ans(Q);


  auto calc_ans = [&](const vector< int > &a, vector< int > b, int k, int base) {
    for(int x : a) {
      if(b.size() >= 30) break;
      for(int y : b) chmin(x, x ^ y);
      if(x) b.emplace_back(x);
    }
    auto &tap = b;
    if(1 << tap.size() <= k) {
      return -1;
    } else {
      sort(tap.begin(), tap.end());
      for(int j = (int) tap.size() - 1; j >= 0; j--) {
        if(k < (1 << j)) {
          chmin(base, base ^ tap[j]);
        } else {
          k -= 1 << j;
          chmax(base, base ^ tap[j]);
        }
      }
      return base;
    }
  };

  auto add_dfs = MFP([&](auto add_dfs, int idx, int par, vector< int > base, int Left, int id) -> void {
    if(weight[idx]) f(base, weight[idx]);
    cash[idx] = base;

    for(auto &q : ev[idx]) {
      if(Left <= last[q] && last[q] < id) ans[q] = calc_ans(cash[X[q]], cash[Y[q]], K[q], Z[q]);
      last[q] = id;
    }

    for(auto &to : t.g[idx]) {
      if(to == par) continue;
      if(used[to]) continue;
      add_dfs(to, idx, base, Left, id);
    }
  });


  MFP([&](auto dfs, int centroid) -> void {
    used[centroid] = true;

    vector< int > base;
    int Left = ptr;
    if(weight[centroid]) base.emplace_back(weight[centroid]);
    for(auto &q : ev[centroid]) {
      if(last[q] == ptr) ans[q] = calc_ans(base, base, K[q], Z[q]);
      last[q] = ptr;
    }
    cash[centroid] = base;
    ++ptr;

    for(auto &to : t.g[centroid]) {
      if(used[to]) continue;
      add_dfs(to, centroid, base, Left, ptr++);
    }

    for(auto &to : ushitapunichia.g[centroid]) {
      dfs(to);
    }
    used[centroid] = false;
  })(root);


  for(auto &p : ans) cout << p << "\n";
}
accuracy: 1
time_ms: 850
memory_kb: 192052
final_rank: 8

code_snippet: #include <bits/stdc++.h>

using namespace std;

using int64 = long long;
const int mod = 1e9 + 7;
// const int mod = 998244353;

const int64 infll = (1LL << 62) - 1;
const int inf = (1 << 30) - 1;

struct IoSetup {
  IoSetup() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(10);
    cerr << fixed << setprecision(10);
  }
} iosetup;


template< typename T1, typename T2 >
ostream &operator<<(ostream &os, const pair< T1, T2 > &p) {
  os << p.first << " " << p.second;
  return os;
}

template< typename T1, typename T2 >
istream &operator>>(istream &is, pair< T1, T2 > &p) {
  is >> p.first >> p.second;
  return is;
}

template< typename T >
ostream &operator<<(ostream &os, const vector< T > &v) {
  for(int i = 0; i < (int) v.size(); i++) {
    os << v[i] << (i + 1 != v.size() ? " " : "");
  }
  return os;
}

template< typename T >
istream &operator>>(istream &is, vector< T > &v) {
  for(T &in : v) is >> in;
  return is;
}

template< typename T1, typename T2 >
inline bool chmax(T1 &a, T2 b) { return a < b && (a = b, true); }

template< typename T1, typename T2 >
inline bool chmin(T1 &a, T2 b) { return a > b && (a = b, true); }

template< typename T = int64 >
vector< T > make_v(size_t a) {
  return vector< T >(a);
}

template< typename T, typename... Ts >
auto make_v(size_t a, Ts... ts) {
  return vector< decltype(make_v< T >(ts...)) >(a, make_v< T >(ts...));
}

template< typename T, typename V >
typename enable_if< is_class< T >::value == 0 >::type fill_v(T &t, const V &v) {
  t = v;
}

template< typename T, typename V >
typename enable_if< is_class< T >::value != 0 >::type fill_v(T &t, const V &v) {
  for(auto &e : t) fill_v(e, v);
}

template< typename F >
struct FixPoint : F {
  FixPoint(F &&f) : F(forward< F >(f)) {}

  template< typename... Args >
  decltype(auto) operator()(Args &&... args) const {
    return F::operator()(*this, forward< Args >(args)...);
  }
};

template< typename F >
inline decltype(auto) MFP(F &&f) {
  return FixPoint< F >{forward< F >(f)};
}

template< typename T >
struct edge {
  int src, to;
  T cost;

  edge(int to, T cost) : src(-1), to(to), cost(cost) {}

  edge(int src, int to, T cost) : src(src), to(to), cost(cost) {}

  edge &operator=(const int &x) {
    to = x;
    return *this;
  }

  operator int() const { return to; }
};

template< typename T >
using Edges = vector< edge< T > >;
template< typename T >
using WeightedGraph = vector< Edges< T > >;
using UnWeightedGraph = vector< vector< int > >;
template< typename T >
using Matrix = vector< vector< T > >;

template< typename G >
struct LowLink {
  const G &g;
  vector< int > used, ord, low;
  vector< int > articulation;
  vector< pair< int, int > > bridge;

  LowLink(const G &g) : g(g) {}

  int dfs(int idx, int k, int par) {
    used[idx] = true;
    ord[idx] = k++;
    low[idx] = ord[idx];
    bool is_articulation = false;
    int cnt = 0;
    for(auto &to : g[idx]) {
      if(!used[to]) {
        ++cnt;
        k = dfs(to, k, idx);
        low[idx] = min(low[idx], low[to]);
        is_articulation |= ~par && low[to] >= ord[idx];
        if(ord[idx] < low[to]) bridge.emplace_back(minmax(idx, (int) to));
      } else if(to != par) {
        low[idx] = min(low[idx], ord[to]);
      }
    }
    is_articulation |= par == -1 && cnt > 1;
    if(is_articulation) articulation.push_back(idx);
    return k;
  }

  virtual void build() {
    used.assign(g.size(), 0);
    ord.assign(g.size(), 0);
    low.assign(g.size(), 0);
    int k = 0;
    for(int i = 0; i < g.size(); i++) {
      if(!used[i]) k = dfs(i, k, -1);
    }
  }
};

template< typename G >
struct BiConnectedComponents : LowLink< G > {
  using LL = LowLink< G >;

  vector< int > used;
  vector< vector< pair< int, int > > > bc;
  vector< pair< int, int > > tmp;

  explicit BiConnectedComponents(const G &g) : LL(g) {}

  void dfs(int idx, int par) {
    used[idx] = true;
    for(auto &to : this->g[idx]) {
      if(to == par) {
        par = -1;
        continue;
      }
      if(!used[to] || this->ord[to] < this->ord[idx]) {
        tmp.emplace_back(minmax(idx, (int) to));
      }
      if(!used[to]) {
        dfs(to, idx);
        if(this->low[to] >= this->ord[idx]) {
          bc.emplace_back();
          for(;;) {
            auto e = tmp.back();
            bc.back().emplace_back(e);
            tmp.pop_back();
            if(e.first == min(idx, (int) to) && e.second == max(idx, (int) to)) {
              break;
            }
          }
        }
      }
    }
  }

  void build() override {
    LL::build();
    used.assign(this->g.size(), 0);
    for(int i = 0; i < used.size(); i++) {
      if(!used[i]) dfs(i, -1);
    }
  }
};

template< typename G >
struct BlockCutTree : BiConnectedComponents< G > {

  using BC = BiConnectedComponents< G >;
  using BC::BiConnectedComponents;

  pair< UnWeightedGraph, vector< int > > bctree() {
    BC::build();
    const int N = (int) BC::g.size();
    auto &bcs = BC::bc;
    vector< int > is_articulation(N, -1), uku(N, -1);
    int ptr = bcs.size();
    for(auto &art : BC::articulation) {
      is_articulation[art] = ptr++;
    }
    vector< int > last(ptr, -1);
    UnWeightedGraph t(ptr);
    for(int k = 0; k < bcs.size(); k++) {
      auto &bc = bcs[k];
      for(auto &e : bc) {
        if(~is_articulation[e.first]) {
          int to = is_articulation[e.first];
          if(last[to] != k) {
            last[to] = k;
            t[to].emplace_back(k);
            t[k].emplace_back(to);
          }
        } else {
          uku[e.first] = k;
        }
        if(~is_articulation[e.second]) {
          int to = is_articulation[e.second];
          if(last[to] != k) {
            last[to] = k;
            t[to].emplace_back(k);
            t[k].emplace_back(to);
          }
        } else {
          uku[e.second] = k;
        }
      }
    }
    for(int i = 0; i < N; i++) uku[i] = max(uku[i], is_articulation[i]);
    return {t, uku};
  }
};

struct UnionFind {
  vector< int > data;

  UnionFind(int sz) {
    data.assign(sz, -1);
  }

  bool unite(int x, int y) {
    x = find(x), y = find(y);
    if(x == y) return (false);
    if(data[x] > data[y]) swap(x, y);
    data[x] += data[y];
    data[y] = x;
    return (true);
  }

  int find(int k) {
    if(data[k] < 0) return (k);
    return (data[k] = find(data[k]));
  }

  int size(int k) {
    return (-data[find(k)]);
  }
};

template< typename G >
struct DoublingLowestCommonAncestor {
  const int LOG;
  vector< int > dep;
  vector< int > sum;
  const G &g;
  vector< vector< int > > table;

  DoublingLowestCommonAncestor(const G &g) : g(g), dep(g.size()), LOG(32 - __builtin_clz(g.size())), sum(g.size()) {
    table.assign(LOG, vector< int >(g.size(), -1));
  }

  void dfs(int idx, int par, int d) {
    table[0][idx] = par;
    dep[idx] = d;
    for(auto &to : g[idx]) {
      if(to != par) {
        sum[to] = sum[idx] ^ to.cost;
        dfs(to, idx, d + 1);
      }
    }
  }

  void build() {
    dfs(0, -1, 0);
    for(int k = 0; k + 1 < LOG; k++) {
      for(int i = 0; i < table[k].size(); i++) {
        if(table[k][i] == -1) table[k + 1][i] = -1;
        else table[k + 1][i] = table[k][table[k][i]];
      }
    }
  }

  int query(int u, int v) {
    if(dep[u] > dep[v]) swap(u, v);
    for(int i = LOG - 1; i >= 0; i--) {
      if(((dep[v] - dep[u]) >> i) & 1) v = table[i][v];
    }
    if(u == v) return u;
    for(int i = LOG - 1; i >= 0; i--) {
      if(table[i][u] != table[i][v]) {
        u = table[i][u];
        v = table[i][v];
      }
    }
    return table[0][u];
  }

  int dist(int a, int b) {
    return sum[a] ^ sum[b];
  }
};


template< typename G >
struct CentroidDecomposition {
  const G &g;
  vector< int > sub;
  vector< vector< int > > belong;
  vector< bool > v;

  CentroidDecomposition(const G &g) : g(g), sub(g.size()), v(g.size()), belong(g.size()) {}

  inline int build_dfs(int idx, int par) {
    sub[idx] = 1;
    for(auto &to : g[idx]) {
      if(to == par || v[to]) continue;
      sub[idx] += build_dfs(to, idx);
    }
    return sub[idx];
  }

  inline int search_centroid(int idx, int par, const int mid) {
    for(auto &to : g[idx]) {
      if(to == par || v[to]) continue;
      if(sub[to] > mid) return search_centroid(to, idx, mid);
    }
    return idx;
  }

  inline void belong_dfs(int idx, int par, int centroid) {
    belong[idx].emplace_back(centroid);
    for(auto &to : g[idx]) {
      if(to == par || v[to]) continue;
      belong_dfs(to, idx, centroid);
    }
  }

  inline int build(UnWeightedGraph &t, int idx) {
    int centroid = search_centroid(idx, -1, build_dfs(idx, -1) / 2);
    v[centroid] = true;
    belong_dfs(centroid, -1, centroid);
    for(auto &to : g[centroid]) {
      if(!v[to]) t[centroid].emplace_back(build(t, to));
    }
    v[centroid] = false;
    return centroid;
  }

  inline int build(UnWeightedGraph &t) {
    t.resize(g.size());
    return build(t, 0);
  }
};


int main() {
  int N, M;
  cin >> N >> M;
  UnWeightedGraph g(N);
  WeightedGraph< int > h(N);
  UnionFind uf(N);
  vector< int > A(M), B(M), C(M);
  for(int i = 0; i < M; i++) {
    cin >> A[i] >> B[i] >> C[i];
    --A[i], --B[i];
    g[A[i]].emplace_back(B[i]);
    g[B[i]].emplace_back(A[i]);
    if(uf.unite(A[i], B[i])) {
      h[A[i]].emplace_back(B[i], C[i]);
      h[B[i]].emplace_back(A[i], C[i]);
    }
  }
  DoublingLowestCommonAncestor< WeightedGraph< int > > beet(h);
  beet.build();
  BlockCutTree< UnWeightedGraph > bct(g);
  UnWeightedGraph t;
  vector< int > rev;
  tie(t, rev) = bct.bctree();
  vector< int > weight(t.size());

  CentroidDecomposition< UnWeightedGraph > cpd(t);
  UnWeightedGraph ushitapunichia;
  int root = cpd.build(ushitapunichia);


  {
    map< pair< int, int >, int > conv;
    for(int i = 0; i < bct.bc.size(); i++) {
      for(auto &p : bct.bc[i]) conv[p] = i;
    }
    for(int i = 0; i < M; i++) {
      weight[conv[minmax(A[i], B[i])]] ^= C[i];
    }
    for(int i = 0; i < t.size(); i++) {
      if(i < bct.bc.size() && bct.bc[i].size() >= 2) continue;
      weight[i] = 0;
    }
  }

  using vi = vector< int >;

  auto f = [](vi &a, int b) {
    for(int y : a) chmin(b, b ^ y);
    if(b) a.emplace_back(b);
  };

  int Q;
  cin >> Q;
  vector< int > X(Q), Y(Q), K(Q), Z(Q);
  for(int i = 0; i < Q; i++) {
    cin >> X[i] >> Y[i] >> K[i];
    --X[i], --Y[i], --K[i];
    Z[i] = beet.dist(X[i], Y[i]);
    X[i] = rev[X[i]];
    Y[i] = rev[Y[i]];
  }

  vector< vector< int > > ev(t.size());
  for(int i = 0; i < Q; i++) {
    ev[X[i]].emplace_back(i);
    ev[Y[i]].emplace_back(i);
  }
  vector< int > used(t.size());
  vector< vector< int > > cash(t.size());
  vector< int > last(Q);
  int ptr = 1;
  vector< int > ans(Q);


  auto calc_ans = [&](const vector< int > &a, vector< int > b, int k, int base) {
    for(int x : a) {
      if(b.size() >= 30) break;
      for(int y : b) chmin(x, x ^ y);
      if(x) b.emplace_back(x);
    }
    auto &tap = b;
    if(1 << tap.size() <= k) {
      return -1;
    } else {
      sort(tap.begin(), tap.end());
      for(int j = (int) tap.size() - 1; j >= 0; j--) {
        if(k < (1 << j)) {
          chmin(base, base ^ tap[j]);
        } else {
          k -= 1 << j;
          chmax(base, base ^ tap[j]);
        }
      }
      return base;
    }
  };

  auto add_dfs = MFP([&](auto add_dfs, int idx, int par, vector< int > base, int Left, int id) -> void {
    if(weight[idx]) f(base, weight[idx]);
    cash[idx] = base;

    for(auto &q : ev[idx]) {
      if(Left <= last[q] && last[q] < id) ans[q] = calc_ans(cash[X[q]], cash[Y[q]], K[q], Z[q]);
      last[q] = id;
    }

    for(auto &to : t[idx]) {
      if(to == par) continue;
      if(used[to]) continue;
      add_dfs(to, idx, base, Left, id);
    }
  });


  MFP([&](auto dfs, int centroid) -> void {
    used[centroid] = true;

    vector< int > base;
    int Left = ptr;
    if(weight[centroid]) base.emplace_back(weight[centroid]);
    for(auto &q : ev[centroid]) {
      if(last[q] == ptr) ans[q] = calc_ans(base, base, K[q], Z[q]);
      last[q] = ptr;
    }
    cash[centroid] = base;
    ++ptr;

    for(auto &to : t[centroid]) {
      if(used[to]) continue;
      add_dfs(to, centroid, vector< int >(), Left, ptr++);
    }

    for(auto &to : ushitapunichia[centroid]) dfs(to);
    used[centroid] = false;
  })(root);


  for(auto &p : ans) cout << p << "\n";
}
accuracy: 0.09523809523809523
time_ms: 680
memory_kb: 134584
final_rank: 20

code_snippet: #include <bits/stdc++.h>

using namespace std;

using int64 = long long;
const int mod = 1e9 + 7;
// const int mod = 998244353;

const int64 infll = (1LL << 62) - 1;
const int inf = (1 << 30) - 1;

struct IoSetup {
  IoSetup() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(10);
    cerr << fixed << setprecision(10);
  }
} iosetup;


template< typename T1, typename T2 >
ostream &operator<<(ostream &os, const pair< T1, T2 > &p) {
  os << p.first << " " << p.second;
  return os;
}

template< typename T1, typename T2 >
istream &operator>>(istream &is, pair< T1, T2 > &p) {
  is >> p.first >> p.second;
  return is;
}

template< typename T >
ostream &operator<<(ostream &os, const vector< T > &v) {
  for(int i = 0; i < (int) v.size(); i++) {
    os << v[i] << (i + 1 != v.size() ? " " : "");
  }
  return os;
}

template< typename T >
istream &operator>>(istream &is, vector< T > &v) {
  for(T &in : v) is >> in;
  return is;
}

template< typename T1, typename T2 >
inline bool chmax(T1 &a, T2 b) { return a < b && (a = b, true); }

template< typename T1, typename T2 >
inline bool chmin(T1 &a, T2 b) { return a > b && (a = b, true); }

template< typename T = int64 >
vector< T > make_v(size_t a) {
  return vector< T >(a);
}

template< typename T, typename... Ts >
auto make_v(size_t a, Ts... ts) {
  return vector< decltype(make_v< T >(ts...)) >(a, make_v< T >(ts...));
}

template< typename T, typename V >
typename enable_if< is_class< T >::value == 0 >::type fill_v(T &t, const V &v) {
  t = v;
}

template< typename T, typename V >
typename enable_if< is_class< T >::value != 0 >::type fill_v(T &t, const V &v) {
  for(auto &e : t) fill_v(e, v);
}

template< typename F >
struct FixPoint : F {
  FixPoint(F &&f) : F(forward< F >(f)) {}

  template< typename... Args >
  decltype(auto) operator()(Args &&... args) const {
    return F::operator()(*this, forward< Args >(args)...);
  }
};

template< typename F >
inline decltype(auto) MFP(F &&f) {
  return FixPoint< F >{forward< F >(f)};
}

template< typename T >
struct edge {
  int src, to;
  T cost;

  edge(int to, T cost) : src(-1), to(to), cost(cost) {}

  edge(int src, int to, T cost) : src(src), to(to), cost(cost) {}

  edge &operator=(const int &x) {
    to = x;
    return *this;
  }

  operator int() const { return to; }
};

template< typename T >
using Edges = vector< edge< T > >;
template< typename T >
using WeightedGraph = vector< Edges< T > >;
using UnWeightedGraph = vector< vector< int > >;
template< typename T >
using Matrix = vector< vector< T > >;

template< typename G >
struct LowLink {
  const G &g;
  vector< int > used, ord, low;
  vector< int > articulation;
  vector< pair< int, int > > bridge;

  LowLink(const G &g) : g(g) {}

  int dfs(int idx, int k, int par) {
    used[idx] = true;
    ord[idx] = k++;
    low[idx] = ord[idx];
    bool is_articulation = false;
    int cnt = 0;
    for(auto &to : g[idx]) {
      if(!used[to]) {
        ++cnt;
        k = dfs(to, k, idx);
        low[idx] = min(low[idx], low[to]);
        is_articulation |= ~par && low[to] >= ord[idx];
        if(ord[idx] < low[to]) bridge.emplace_back(minmax(idx, (int) to));
      } else if(to != par) {
        low[idx] = min(low[idx], ord[to]);
      }
    }
    is_articulation |= par == -1 && cnt > 1;
    if(is_articulation) articulation.push_back(idx);
    return k;
  }

  virtual void build() {
    used.assign(g.size(), 0);
    ord.assign(g.size(), 0);
    low.assign(g.size(), 0);
    int k = 0;
    for(int i = 0; i < g.size(); i++) {
      if(!used[i]) k = dfs(i, k, -1);
    }
  }
};

template< typename G >
struct BiConnectedComponents : LowLink< G > {
  using LL = LowLink< G >;

  vector< int > used;
  vector< vector< pair< int, int > > > bc;
  vector< pair< int, int > > tmp;

  explicit BiConnectedComponents(const G &g) : LL(g) {}

  void dfs(int idx, int par) {
    used[idx] = true;
    for(auto &to : this->g[idx]) {
      if(to == par) {
        par = -1;
        continue;
      }
      if(!used[to] || this->ord[to] < this->ord[idx]) {
        tmp.emplace_back(minmax(idx, (int) to));
      }
      if(!used[to]) {
        dfs(to, idx);
        if(this->low[to] >= this->ord[idx]) {
          bc.emplace_back();
          for(;;) {
            auto e = tmp.back();
            bc.back().emplace_back(e);
            tmp.pop_back();
            if(e.first == min(idx, (int) to) && e.second == max(idx, (int) to)) {
              break;
            }
          }
        }
      }
    }
  }

  void build() override {
    LL::build();
    used.assign(this->g.size(), 0);
    for(int i = 0; i < used.size(); i++) {
      if(!used[i]) dfs(i, -1);
    }
  }
};

template< typename G >
struct BlockCutTree : BiConnectedComponents< G > {

  using BC = BiConnectedComponents< G >;
  using BC::BiConnectedComponents;

  pair< UnWeightedGraph, vector< int > > bctree() {
    BC::build();
    const int N = (int) BC::g.size();
    auto &bcs = BC::bc;
    vector< int > is_articulation(N, -1), uku(N, -1);
    int ptr = bcs.size();
    for(auto &art : BC::articulation) {
      is_articulation[art] = ptr++;
    }
    vector< int > last(ptr, -1);
    UnWeightedGraph t(ptr);
    for(int k = 0; k < bcs.size(); k++) {
      auto &bc = bcs[k];
      for(auto &e : bc) {
        if(~is_articulation[e.first]) {
          int to = is_articulation[e.first];
          if(last[to] != k) {
            last[to] = k;
            t[to].emplace_back(k);
            t[k].emplace_back(to);
          }
        } else {
          uku[e.first] = k;
        }
        if(~is_articulation[e.second]) {
          int to = is_articulation[e.second];
          if(last[to] != k) {
            last[to] = k;
            t[to].emplace_back(k);
            t[k].emplace_back(to);
          }
        } else {
          uku[e.second] = k;
        }
      }
    }
    for(int i = 0; i < N; i++) uku[i] = max(uku[i], is_articulation[i]);
    return {t, uku};
  }
};

struct UnionFind {
  vector< int > data;

  UnionFind(int sz) {
    data.assign(sz, -1);
  }

  bool unite(int x, int y) {
    x = find(x), y = find(y);
    if(x == y) return (false);
    if(data[x] > data[y]) swap(x, y);
    data[x] += data[y];
    data[y] = x;
    return (true);
  }

  int find(int k) {
    if(data[k] < 0) return (k);
    return (data[k] = find(data[k]));
  }

  int size(int k) {
    return (-data[find(k)]);
  }
};

template< typename G >
struct DoublingLowestCommonAncestor {
  const int LOG;
  vector< int > dep;
  vector< int > sum;
  const G &g;
  vector< vector< int > > table;

  DoublingLowestCommonAncestor(const G &g) : g(g), dep(g.size()), LOG(32 - __builtin_clz(g.size())), sum(g.size()) {
    table.assign(LOG, vector< int >(g.size(), -1));
  }

  void dfs(int idx, int par, int d) {
    table[0][idx] = par;
    dep[idx] = d;
    for(auto &to : g[idx]) {
      if(to != par) {
        sum[to] = sum[idx] ^ to.cost;
        dfs(to, idx, d + 1);
      }
    }
  }

  void build() {
    dfs(0, -1, 0);
    for(int k = 0; k + 1 < LOG; k++) {
      for(int i = 0; i < table[k].size(); i++) {
        if(table[k][i] == -1) table[k + 1][i] = -1;
        else table[k + 1][i] = table[k][table[k][i]];
      }
    }
  }

  int query(int u, int v) {
    if(dep[u] > dep[v]) swap(u, v);
    for(int i = LOG - 1; i >= 0; i--) {
      if(((dep[v] - dep[u]) >> i) & 1) v = table[i][v];
    }
    if(u == v) return u;
    for(int i = LOG - 1; i >= 0; i--) {
      if(table[i][u] != table[i][v]) {
        u = table[i][u];
        v = table[i][v];
      }
    }
    return table[0][u];
  }

  int dist(int a, int b) {
    return sum[a] ^ sum[b];
  }
};


template< typename G >
struct CentroidDecomposition {
  const G &g;
  vector< int > sub;
  vector< vector< int > > belong;
  vector< bool > v;

  CentroidDecomposition(const G &g) : g(g), sub(g.size()), v(g.size()), belong(g.size()) {}

  inline int build_dfs(int idx, int par) {
    sub[idx] = 1;
    for(auto &to : g[idx]) {
      if(to == par || v[to]) continue;
      sub[idx] += build_dfs(to, idx);
    }
    return sub[idx];
  }

  inline int search_centroid(int idx, int par, const int mid) {
    for(auto &to : g[idx]) {
      if(to == par || v[to]) continue;
      if(sub[to] > mid) return search_centroid(to, idx, mid);
    }
    return idx;
  }

  inline void belong_dfs(int idx, int par, int centroid) {
    belong[idx].emplace_back(centroid);
    for(auto &to : g[idx]) {
      if(to == par || v[to]) continue;
      belong_dfs(to, idx, centroid);
    }
  }

  inline int build(UnWeightedGraph &t, int idx) {
    int centroid = search_centroid(idx, -1, build_dfs(idx, -1) / 2);
    v[centroid] = true;
    belong_dfs(centroid, -1, centroid);
    for(auto &to : g[centroid]) {
      if(!v[to]) t[centroid].emplace_back(build(t, to));
    }
    v[centroid] = false;
    return centroid;
  }

  inline int build(UnWeightedGraph &t) {
    t.resize(g.size());
    return build(t, 0);
  }
};


int main() {
  int N, M;
  cin >> N >> M;
  UnWeightedGraph g(N);
  WeightedGraph< int > h(N);
  UnionFind uf(N);
  vector< int > A(M), B(M), C(M);
  for(int i = 0; i < M; i++) {
    cin >> A[i] >> B[i] >> C[i];
    --A[i], --B[i];
    g[A[i]].emplace_back(B[i]);
    g[B[i]].emplace_back(A[i]);
    if(uf.unite(A[i], B[i])) {
      h[A[i]].emplace_back(B[i], C[i]);
      h[B[i]].emplace_back(A[i], C[i]);
    }
  }
  DoublingLowestCommonAncestor< WeightedGraph< int > > beet(h);
  beet.build();
  BlockCutTree< UnWeightedGraph > bct(g);
  UnWeightedGraph t;
  vector< int > rev;
  tie(t, rev) = bct.bctree();
  vector< int > weight(t.size());

  CentroidDecomposition< UnWeightedGraph > cpd(t);
  UnWeightedGraph ushitapunichia;
  int root = cpd.build(ushitapunichia);


  {
    map< pair< int, int >, int > conv;
    for(int i = 0; i < bct.bc.size(); i++) {
      for(auto &p : bct.bc[i]) conv[p] = i;
    }
    for(int i = 0; i < M; i++) {
      weight[conv[minmax(A[i], B[i])]] ^= C[i];
    }
    for(int i = 0; i < t.size(); i++) {
      if(i < bct.bc.size() && bct.bc[i].size() >= 2) continue;
      weight[i] = 0;
    }
  }

  using vi = vector< int >;

  auto f = [](vi &a, int b) {
    for(int y : a) chmin(b, b ^ y);
    if(b) a.emplace_back(b);
  };

  int Q;
  cin >> Q;
  vector< int > X(Q), Y(Q), K(Q), Z(Q);
  for(int i = 0; i < Q; i++) {
    cin >> X[i] >> Y[i] >> K[i];
    --X[i], --Y[i], --K[i];
    Z[i] = beet.dist(X[i], Y[i]);
    X[i] = rev[X[i]];
    Y[i] = rev[Y[i]];
  }

  vector< vector< int > > ev(t.size());
  for(int i = 0; i < Q; i++) {
    ev[X[i]].emplace_back(i);
    ev[Y[i]].emplace_back(i);
  }
  vector< int > used(t.size());
  vector< vector< int > > cash(t.size());
  vector< int > last(Q);
  int ptr = 1;
  vector< int > ans(Q);


  auto calc_ans = [&](const vector< int > &a, vector< int > b, int k, int base) {
    for(int x : a) {
      if(b.size() >= 30) break;
      for(int y : b) chmin(x, x ^ y);
      if(x) b.emplace_back(x);
    }
    auto &tap = b;
    if(1 << tap.size() <= k) {
      return -1;
    } else {
      sort(tap.begin(), tap.end());
      for(int j = (int) tap.size() - 1; j >= 0; j--) {
        if(k < (1 << j)) {
          chmin(base, base ^ tap[j]);
        } else {
          k -= 1 << j;
          chmax(base, base ^ tap[j]);
        }
      }
      return base;
    }
  };

  auto add_dfs = MFP([&](auto add_dfs, int idx, int par, vector< int > base, int Left, int id) -> void {
    if(weight[idx]) f(base, weight[idx]);
    cash[idx] = base;

    for(auto &q : ev[idx]) {
      if(Left <= last[q] && last[q] < id) ans[q] = calc_ans(cash[X[q]], cash[Y[q]], K[q], Z[q]);
      last[q] = id;
    }

    for(auto &to : t[idx]) {
      if(to == par) continue;
      if(used[to]) continue;
      add_dfs(to, idx, base, Left, id);
    }
  });


  MFP([&](auto dfs, int centroid) -> void {
    used[centroid] = true;

    vector< int > base;
    int Left = ptr;
    if(weight[centroid]) base.emplace_back(weight[centroid]);
    for(auto &q : ev[centroid]) {
      if(last[q] == ptr) ans[q] = calc_ans(base, base, K[q], Z[q]);
      last[q] = ptr;
    }
    cash[centroid] = base;
    ++ptr;

    for(auto &to : t[centroid]) {
      if(used[to]) continue;
      add_dfs(to, centroid, base, Left, ptr++);
    }

    for(auto &to : ushitapunichia[centroid]) dfs(to);
    used[centroid] = false;
  })(root);


  for(auto &p : ans) cout << p << "\n";
}
accuracy: 1
time_ms: 900
memory_kb: 133168
final_rank: 4

code_snippet: #include <bits/stdc++.h>

using namespace std;

using int64 = long long;
const int mod = 1e9 + 7;
// const int mod = 998244353;

const int64 infll = (1LL << 62) - 1;
const int inf = (1 << 30) - 1;

struct IoSetup {
  IoSetup() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(10);
    cerr << fixed << setprecision(10);
  }
} iosetup;


template< typename T1, typename T2 >
ostream &operator<<(ostream &os, const pair< T1, T2 > &p) {
  os << p.first << " " << p.second;
  return os;
}

template< typename T1, typename T2 >
istream &operator>>(istream &is, pair< T1, T2 > &p) {
  is >> p.first >> p.second;
  return is;
}

template< typename T >
ostream &operator<<(ostream &os, const vector< T > &v) {
  for(int i = 0; i < (int) v.size(); i++) {
    os << v[i] << (i + 1 != v.size() ? " " : "");
  }
  return os;
}

template< typename T >
istream &operator>>(istream &is, vector< T > &v) {
  for(T &in : v) is >> in;
  return is;
}

template< typename T1, typename T2 >
inline bool chmax(T1 &a, T2 b) { return a < b && (a = b, true); }

template< typename T1, typename T2 >
inline bool chmin(T1 &a, T2 b) { return a > b && (a = b, true); }

template< typename T = int64 >
vector< T > make_v(size_t a) {
  return vector< T >(a);
}

template< typename T, typename... Ts >
auto make_v(size_t a, Ts... ts) {
  return vector< decltype(make_v< T >(ts...)) >(a, make_v< T >(ts...));
}

template< typename T, typename V >
typename enable_if< is_class< T >::value == 0 >::type fill_v(T &t, const V &v) {
  t = v;
}

template< typename T, typename V >
typename enable_if< is_class< T >::value != 0 >::type fill_v(T &t, const V &v) {
  for(auto &e : t) fill_v(e, v);
}

template< typename F >
struct FixPoint : F {
  FixPoint(F &&f) : F(forward< F >(f)) {}

  template< typename... Args >
  decltype(auto) operator()(Args &&... args) const {
    return F::operator()(*this, forward< Args >(args)...);
  }
};

template< typename F >
inline decltype(auto) MFP(F &&f) {
  return FixPoint< F >{forward< F >(f)};
}

template< typename T >
struct edge {
  int src, to;
  T cost;

  edge(int to, T cost) : src(-1), to(to), cost(cost) {}

  edge(int src, int to, T cost) : src(src), to(to), cost(cost) {}

  edge &operator=(const int &x) {
    to = x;
    return *this;
  }

  operator int() const { return to; }
};

template< typename T >
using Edges = vector< edge< T > >;
template< typename T >
using WeightedGraph = vector< Edges< T > >;
using UnWeightedGraph = vector< vector< int > >;
template< typename T >
using Matrix = vector< vector< T > >;

template< typename G >
struct LowLink {
  const G &g;
  vector< int > used, ord, low;
  vector< int > articulation;
  vector< pair< int, int > > bridge;

  LowLink(const G &g) : g(g) {}

  int dfs(int idx, int k, int par) {
    used[idx] = true;
    ord[idx] = k++;
    low[idx] = ord[idx];
    bool is_articulation = false;
    int cnt = 0;
    for(auto &to : g[idx]) {
      if(!used[to]) {
        ++cnt;
        k = dfs(to, k, idx);
        low[idx] = min(low[idx], low[to]);
        is_articulation |= ~par && low[to] >= ord[idx];
        if(ord[idx] < low[to]) bridge.emplace_back(minmax(idx, (int) to));
      } else if(to != par) {
        low[idx] = min(low[idx], ord[to]);
      }
    }
    is_articulation |= par == -1 && cnt > 1;
    if(is_articulation) articulation.push_back(idx);
    return k;
  }

  virtual void build() {
    used.assign(g.size(), 0);
    ord.assign(g.size(), 0);
    low.assign(g.size(), 0);
    int k = 0;
    for(int i = 0; i < g.size(); i++) {
      if(!used[i]) k = dfs(i, k, -1);
    }
  }
};

template< typename G >
struct BiConnectedComponents : LowLink< G > {
  using LL = LowLink< G >;

  vector< int > used;
  vector< vector< pair< int, int > > > bc;
  vector< pair< int, int > > tmp;

  explicit BiConnectedComponents(const G &g) : LL(g) {}

  void dfs(int idx, int par) {
    used[idx] = true;
    for(auto &to : this->g[idx]) {
      if(to == par) {
        par = -1;
        continue;
      }
      if(!used[to] || this->ord[to] < this->ord[idx]) {
        tmp.emplace_back(minmax(idx, (int) to));
      }
      if(!used[to]) {
        dfs(to, idx);
        if(this->low[to] >= this->ord[idx]) {
          bc.emplace_back();
          for(;;) {
            auto e = tmp.back();
            bc.back().emplace_back(e);
            tmp.pop_back();
            if(e.first == min(idx, (int) to) && e.second == max(idx, (int) to)) {
              break;
            }
          }
        }
      }
    }
  }

  void build() override {
    LL::build();
    used.assign(this->g.size(), 0);
    for(int i = 0; i < used.size(); i++) {
      if(!used[i]) dfs(i, -1);
    }
  }
};

template< typename G >
struct BlockCutTree : BiConnectedComponents< G > {

  using BC = BiConnectedComponents< G >;
  using BC::BiConnectedComponents;

  pair< UnWeightedGraph, vector< int > > bctree() {
    BC::build();
    const int N = (int) BC::g.size();
    auto &bcs = BC::bc;
    vector< int > is_articulation(N, -1), uku(N, -1);
    int ptr = bcs.size();
    for(auto &art : BC::articulation) {
      is_articulation[art] = ptr++;
    }
    vector< int > last(ptr, -1);
    UnWeightedGraph t(ptr);
    for(int k = 0; k < bcs.size(); k++) {
      auto &bc = bcs[k];
      for(auto &e : bc) {
        if(~is_articulation[e.first]) {
          int to = is_articulation[e.first];
          if(last[to] != k) {
            last[to] = k;
            t[to].emplace_back(k);
            t[k].emplace_back(to);
          }
        } else {
          uku[e.first] = k;
        }
        if(~is_articulation[e.second]) {
          int to = is_articulation[e.second];
          if(last[to] != k) {
            last[to] = k;
            t[to].emplace_back(k);
            t[k].emplace_back(to);
          }
        } else {
          uku[e.second] = k;
        }
      }
    }
    for(int i = 0; i < N; i++) uku[i] = max(uku[i], is_articulation[i]);
    return {t, uku};
  }
};

struct UnionFind {
  vector< int > data;

  UnionFind(int sz) {
    data.assign(sz, -1);
  }

  bool unite(int x, int y) {
    x = find(x), y = find(y);
    if(x == y) return (false);
    if(data[x] > data[y]) swap(x, y);
    data[x] += data[y];
    data[y] = x;
    return (true);
  }

  int find(int k) {
    if(data[k] < 0) return (k);
    return (data[k] = find(data[k]));
  }

  int size(int k) {
    return (-data[find(k)]);
  }
};

template< typename G >
struct DoublingLowestCommonAncestor {
  const int LOG;
  vector< int > dep;
  vector< int > sum;
  const G &g;
  vector< vector< int > > table;

  DoublingLowestCommonAncestor(const G &g) : g(g), dep(g.size()), LOG(32 - __builtin_clz(g.size())), sum(g.size()) {
    table.assign(LOG, vector< int >(g.size(), -1));
  }

  void dfs(int idx, int par, int d) {
    table[0][idx] = par;
    dep[idx] = d;
    for(auto &to : g[idx]) {
      if(to != par) {
        sum[to] = sum[idx] ^ to.cost;
        dfs(to, idx, d + 1);
      }
    }
  }

  void build() {
    dfs(0, -1, 0);
    for(int k = 0; k + 1 < LOG; k++) {
      for(int i = 0; i < table[k].size(); i++) {
        if(table[k][i] == -1) table[k + 1][i] = -1;
        else table[k + 1][i] = table[k][table[k][i]];
      }
    }
  }

  int query(int u, int v) {
    if(dep[u] > dep[v]) swap(u, v);
    for(int i = LOG - 1; i >= 0; i--) {
      if(((dep[v] - dep[u]) >> i) & 1) v = table[i][v];
    }
    if(u == v) return u;
    for(int i = LOG - 1; i >= 0; i--) {
      if(table[i][u] != table[i][v]) {
        u = table[i][u];
        v = table[i][v];
      }
    }
    return table[0][u];
  }

  int dist(int a, int b) {
    return sum[a] ^ sum[b];
  }
};


template< typename G >
struct CentroidDecomposition {
  const G &g;
  vector< int > sub;
  vector< vector< int > > belong;
  vector< bool > v;

  CentroidDecomposition(const G &g) : g(g), sub(g.size()), v(g.size()), belong(g.size()) {}

  inline int build_dfs(int idx, int par) {
    sub[idx] = 1;
    for(auto &to : g[idx]) {
      if(to == par || v[to]) continue;
      sub[idx] += build_dfs(to, idx);
    }
    return sub[idx];
  }

  inline int search_centroid(int idx, int par, const int mid) {
    for(auto &to : g[idx]) {
      if(to == par || v[to]) continue;
      if(sub[to] > mid) return search_centroid(to, idx, mid);
    }
    return idx;
  }

  inline void belong_dfs(int idx, int par, int centroid) {
    belong[idx].emplace_back(centroid);
    for(auto &to : g[idx]) {
      if(to == par || v[to]) continue;
      belong_dfs(to, idx, centroid);
    }
  }

  inline int build(UnWeightedGraph &t, int idx) {
    int centroid = search_centroid(idx, -1, build_dfs(idx, -1) / 2);
    v[centroid] = true;
    belong_dfs(centroid, -1, centroid);
    for(auto &to : g[centroid]) {
      if(!v[to]) t[centroid].emplace_back(build(t, to));
    }
    v[centroid] = false;
    return centroid;
  }

  inline int build(UnWeightedGraph &t) {
    t.resize(g.size());
    return build(t, 0);
  }
};


int main() {
  int N, M;
  cin >> N >> M;
  UnWeightedGraph g(N);
  WeightedGraph< int > h(N);
  UnionFind uf(N);
  vector< int > A(M), B(M), C(M);
  for(int i = 0; i < M; i++) {
    cin >> A[i] >> B[i] >> C[i];
    --A[i], --B[i];
    g[A[i]].emplace_back(B[i]);
    g[B[i]].emplace_back(A[i]);
    if(uf.unite(A[i], B[i])) {
      h[A[i]].emplace_back(B[i], C[i]);
      h[B[i]].emplace_back(A[i], C[i]);
    }
  }
  DoublingLowestCommonAncestor< WeightedGraph< int > > beet(h);
  beet.build();
  BlockCutTree< UnWeightedGraph > bct(g);
  UnWeightedGraph t;
  vector< int > rev;
  tie(t, rev) = bct.bctree();
  vector< int > weight(t.size());

  CentroidDecomposition< UnWeightedGraph > cpd(t);
  UnWeightedGraph ushitapunichia;
  int root = cpd.build(ushitapunichia);


  {
    map< pair< int, int >, int > conv;
    for(int i = 0; i < bct.bc.size(); i++) {
      for(auto &p : bct.bc[i]) conv[p] = i;
    }
    for(int i = 0; i < M; i++) {
      weight[conv[minmax(A[i], B[i])]] ^= C[i];
    }
    for(int i = 0; i < t.size(); i++) {
      if(i < bct.bc.size() && bct.bc[i].size() >= 2) continue;
      weight[i] = 0;
    }
  }

  using vi = vector< int >;

  auto f = [](vi &a, int b) {
    for(int y : a) chmin(b, b ^ y);
    if(b) a.emplace_back(b);
  };

  int Q;
  cin >> Q;
  vector< int > X(Q), Y(Q), K(Q), Z(Q);
  for(int i = 0; i < Q; i++) {
    cin >> X[i] >> Y[i] >> K[i];
    --X[i], --Y[i], --K[i];
    Z[i] = beet.dist(X[i], Y[i]);
    X[i] = rev[X[i]];
    Y[i] = rev[Y[i]];
  }

  vector< vector< int > > ev(t.size());
  for(int i = 0; i < Q; i++) {
    ev[X[i]].emplace_back(i);
    ev[Y[i]].emplace_back(i);
  }
  vector< int > used(t.size());
  vector< vector< int > > cash(t.size());
  vector< int > last(Q);
  int ptr = 1;
  vector< int > ans(Q);


  auto calc_ans = [&](const vector< int > &a, vector< int > b, int k, int base) {
    for(int x : a) {
      for(int y : b) chmin(x, x ^ y);
      if(x) b.emplace_back(x);
    }
    auto &tap = b;
    if(1 << tap.size() <= k) {
      return -1;
    } else {
      sort(tap.begin(), tap.end());
      for(int j = (int) tap.size() - 1; j >= 0; j--) {
        if(k < (1 << j)) {
          chmin(base, base ^ tap[j]);
        } else {
          k -= 1 << j;
          chmax(base, base ^ tap[j]);
        }
      }
      return base;
    }
  };

  auto add_dfs = MFP([&](auto add_dfs, int idx, int par, vector< int > base, int Left, int id) -> void {
    if(weight[idx]) f(base, weight[idx]);
    cash[idx] = base;

    for(auto &q : ev[idx]) {
      if(Left <= last[q] && last[q] < id) ans[q] = calc_ans(cash[X[q]], cash[Y[q]], K[q], Z[q]);
      last[q] = id;
    }

    for(auto &to : t[idx]) {
      if(to == par) continue;
      if(used[to]) continue;
      add_dfs(to, idx, base, Left, id);
    }
  });


  MFP([&](auto dfs, int centroid) -> void {
    used[centroid] = true;

    vector< int > base;
    int Left = ptr;
    if(weight[centroid]) base.emplace_back(weight[centroid]);
    for(auto &q : ev[centroid]) {
      if(last[q] == ptr) ans[q] = calc_ans(base, base, K[q], Z[q]);
      last[q] = ptr;
    }
    cash[centroid] = base;
    ++ptr;

    for(auto &to : t[centroid]) {
      if(used[to]) continue;
      add_dfs(to, centroid, base, Left, ptr++);
    }

    for(auto &to : ushitapunichia[centroid]) dfs(to);
    used[centroid] = false;
  })(root);


  for(auto &p : ans) cout << p << "\n";
}
accuracy: 1
time_ms: 950
memory_kb: 132904
final_rank: 6

code_snippet: #include <bits/stdc++.h>

using namespace std;

using int64 = long long;
const int mod = 1e9 + 7;
// const int mod = 998244353;

const int64 infll = (1LL << 62) - 1;
const int inf = (1 << 30) - 1;

struct IoSetup {
  IoSetup() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(10);
    cerr << fixed << setprecision(10);
  }
} iosetup;


template< typename T1, typename T2 >
ostream &operator<<(ostream &os, const pair< T1, T2 > &p) {
  os << p.first << " " << p.second;
  return os;
}

template< typename T1, typename T2 >
istream &operator>>(istream &is, pair< T1, T2 > &p) {
  is >> p.first >> p.second;
  return is;
}

template< typename T >
ostream &operator<<(ostream &os, const vector< T > &v) {
  for(int i = 0; i < (int) v.size(); i++) {
    os << v[i] << (i + 1 != v.size() ? " " : "");
  }
  return os;
}

template< typename T >
istream &operator>>(istream &is, vector< T > &v) {
  for(T &in : v) is >> in;
  return is;
}

template< typename T1, typename T2 >
inline bool chmax(T1 &a, T2 b) { return a < b && (a = b, true); }

template< typename T1, typename T2 >
inline bool chmin(T1 &a, T2 b) { return a > b && (a = b, true); }

template< typename T = int64 >
vector< T > make_v(size_t a) {
  return vector< T >(a);
}

template< typename T, typename... Ts >
auto make_v(size_t a, Ts... ts) {
  return vector< decltype(make_v< T >(ts...)) >(a, make_v< T >(ts...));
}

template< typename T, typename V >
typename enable_if< is_class< T >::value == 0 >::type fill_v(T &t, const V &v) {
  t = v;
}

template< typename T, typename V >
typename enable_if< is_class< T >::value != 0 >::type fill_v(T &t, const V &v) {
  for(auto &e : t) fill_v(e, v);
}

template< typename F >
struct FixPoint : F {
  FixPoint(F &&f) : F(forward< F >(f)) {}

  template< typename... Args >
  decltype(auto) operator()(Args &&... args) const {
    return F::operator()(*this, forward< Args >(args)...);
  }
};

template< typename F >
inline decltype(auto) MFP(F &&f) {
  return FixPoint< F >{forward< F >(f)};
}

template< typename T >
struct edge {
  int src, to;
  T cost;

  edge(int to, T cost) : src(-1), to(to), cost(cost) {}

  edge(int src, int to, T cost) : src(src), to(to), cost(cost) {}

  edge &operator=(const int &x) {
    to = x;
    return *this;
  }

  operator int() const { return to; }
};

template< typename T >
using Edges = vector< edge< T > >;
template< typename T >
using WeightedGraph = vector< Edges< T > >;
using UnWeightedGraph = vector< vector< int > >;
template< typename T >
using Matrix = vector< vector< T > >;

template< typename G >
struct LowLink {
  const G &g;
  vector< int > used, ord, low;
  vector< int > articulation;
  vector< pair< int, int > > bridge;

  LowLink(const G &g) : g(g) {}

  int dfs(int idx, int k, int par) {
    used[idx] = true;
    ord[idx] = k++;
    low[idx] = ord[idx];
    bool is_articulation = false;
    int cnt = 0;
    for(auto &to : g[idx]) {
      if(!used[to]) {
        ++cnt;
        k = dfs(to, k, idx);
        low[idx] = min(low[idx], low[to]);
        is_articulation |= ~par && low[to] >= ord[idx];
        if(ord[idx] < low[to]) bridge.emplace_back(minmax(idx, (int) to));
      } else if(to != par) {
        low[idx] = min(low[idx], ord[to]);
      }
    }
    is_articulation |= par == -1 && cnt > 1;
    if(is_articulation) articulation.push_back(idx);
    return k;
  }

  virtual void build() {
    used.assign(g.size(), 0);
    ord.assign(g.size(), 0);
    low.assign(g.size(), 0);
    int k = 0;
    for(int i = 0; i < g.size(); i++) {
      if(!used[i]) k = dfs(i, k, -1);
    }
  }
};

template< typename G >
struct BiConnectedComponents : LowLink< G > {
  using LL = LowLink< G >;

  vector< int > used;
  vector< vector< pair< int, int > > > bc;
  vector< pair< int, int > > tmp;

  explicit BiConnectedComponents(const G &g) : LL(g) {}

  void dfs(int idx, int par) {
    used[idx] = true;
    for(auto &to : this->g[idx]) {
      if(to == par) {
        par = -1;
        continue;
      }
      if(!used[to] || this->ord[to] < this->ord[idx]) {
        tmp.emplace_back(minmax(idx, (int) to));
      }
      if(!used[to]) {
        dfs(to, idx);
        if(this->low[to] >= this->ord[idx]) {
          bc.emplace_back();
          for(;;) {
            auto e = tmp.back();
            bc.back().emplace_back(e);
            tmp.pop_back();
            if(e.first == min(idx, (int) to) && e.second == max(idx, (int) to)) {
              break;
            }
          }
        }
      }
    }
  }

  void build() override {
    LL::build();
    used.assign(this->g.size(), 0);
    for(int i = 0; i < used.size(); i++) {
      if(!used[i]) dfs(i, -1);
    }
  }
};

template< typename G >
struct BlockCutTree : BiConnectedComponents< G > {

  using BC = BiConnectedComponents< G >;
  using BC::BiConnectedComponents;

  pair< UnWeightedGraph, vector< int > > bctree() {
    BC::build();
    const int N = (int) BC::g.size();
    auto &bcs = BC::bc;
    vector< int > is_articulation(N, -1), uku(N, -1);
    int ptr = bcs.size();
    for(auto &art : BC::articulation) {
      is_articulation[art] = ptr++;
    }
    vector< int > last(ptr, -1);
    UnWeightedGraph t(ptr);
    for(int k = 0; k < bcs.size(); k++) {
      auto &bc = bcs[k];
      for(auto &e : bc) {
        if(~is_articulation[e.first]) {
          int to = is_articulation[e.first];
          if(last[to] != k) {
            last[to] = k;
            t[to].emplace_back(k);
            t[k].emplace_back(to);
          }
        } else {
          uku[e.first] = k;
        }
        if(~is_articulation[e.second]) {
          int to = is_articulation[e.second];
          if(last[to] != k) {
            last[to] = k;
            t[to].emplace_back(k);
            t[k].emplace_back(to);
          }
        } else {
          uku[e.second] = k;
        }
      }
    }
    for(int i = 0; i < N; i++) uku[i] = max(uku[i], is_articulation[i]);
    return {t, uku};
  }
};

struct UnionFind {
  vector< int > data;

  UnionFind(int sz) {
    data.assign(sz, -1);
  }

  bool unite(int x, int y) {
    x = find(x), y = find(y);
    if(x == y) return (false);
    if(data[x] > data[y]) swap(x, y);
    data[x] += data[y];
    data[y] = x;
    return (true);
  }

  int find(int k) {
    if(data[k] < 0) return (k);
    return (data[k] = find(data[k]));
  }

  int size(int k) {
    return (-data[find(k)]);
  }
};

template< typename G >
struct DoublingLowestCommonAncestor {
  const int LOG;
  vector< int > dep;
  vector< int > sum;
  const G &g;
  vector< vector< int > > table;

  DoublingLowestCommonAncestor(const G &g) : g(g), dep(g.size()), LOG(32 - __builtin_clz(g.size())), sum(g.size()) {
    table.assign(LOG, vector< int >(g.size(), -1));
  }

  void dfs(int idx, int par, int d) {
    table[0][idx] = par;
    dep[idx] = d;
    for(auto &to : g[idx]) {
      if(to != par) {
        sum[to] = sum[idx] ^ to.cost;
        dfs(to, idx, d + 1);
      }
    }
  }

  void build() {
    dfs(0, -1, 0);
    for(int k = 0; k + 1 < LOG; k++) {
      for(int i = 0; i < table[k].size(); i++) {
        if(table[k][i] == -1) table[k + 1][i] = -1;
        else table[k + 1][i] = table[k][table[k][i]];
      }
    }
  }

  int query(int u, int v) {
    if(dep[u] > dep[v]) swap(u, v);
    for(int i = LOG - 1; i >= 0; i--) {
      if(((dep[v] - dep[u]) >> i) & 1) v = table[i][v];
    }
    if(u == v) return u;
    for(int i = LOG - 1; i >= 0; i--) {
      if(table[i][u] != table[i][v]) {
        u = table[i][u];
        v = table[i][v];
      }
    }
    return table[0][u];
  }

  int dist(int a, int b) {
    return sum[a] ^ sum[b];
  }
};


template< typename G >
struct CentroidDecomposition {
  const G &g;
  vector< int > sub;
  vector< vector< int > > belong;
  vector< bool > v;

  CentroidDecomposition(const G &g) : g(g), sub(g.size()), v(g.size()), belong(g.size()) {}

  inline int build_dfs(int idx, int par) {
    sub[idx] = 1;
    for(auto &to : g[idx]) {
      if(to == par || v[to]) continue;
      sub[idx] += build_dfs(to, idx);
    }
    return sub[idx];
  }

  inline int search_centroid(int idx, int par, const int mid) {
    for(auto &to : g[idx]) {
      if(to == par || v[to]) continue;
      if(sub[to] > mid) return search_centroid(to, idx, mid);
    }
    return idx;
  }

  inline void belong_dfs(int idx, int par, int centroid) {
    belong[idx].emplace_back(centroid);
    for(auto &to : g[idx]) {
      if(to == par || v[to]) continue;
      belong_dfs(to, idx, centroid);
    }
  }

  inline int build(UnWeightedGraph &t, int idx) {
    int centroid = search_centroid(idx, -1, build_dfs(idx, -1) / 2);
    v[centroid] = true;
    belong_dfs(centroid, -1, centroid);
    for(auto &to : g[centroid]) {
      if(!v[to]) t[centroid].emplace_back(build(t, to));
    }
    v[centroid] = false;
    return centroid;
  }

  inline int build(UnWeightedGraph &t) {
    t.resize(g.size());
    return build(t, 0);
  }
};


int main() {
  int N, M;
  cin >> N >> M;
  UnWeightedGraph g(N);
  WeightedGraph< int > h(N);
  UnionFind uf(N);
  vector< int > A(M), B(M), C(M);
  for(int i = 0; i < M; i++) {
    cin >> A[i] >> B[i] >> C[i];
    --A[i], --B[i];
    g[A[i]].emplace_back(B[i]);
    g[B[i]].emplace_back(A[i]);
    if(uf.unite(A[i], B[i])) {
      h[A[i]].emplace_back(B[i], C[i]);
      h[B[i]].emplace_back(A[i], C[i]);
    }
  }
  DoublingLowestCommonAncestor< WeightedGraph< int > > beet(h);
  beet.build();
  BlockCutTree< UnWeightedGraph > bct(g);
  UnWeightedGraph t;
  vector< int > rev;
  tie(t, rev) = bct.bctree();
  vector< int > weight(t.size());

  CentroidDecomposition< UnWeightedGraph > cpd(t);
  UnWeightedGraph ushitapunichia;
  int root = cpd.build(ushitapunichia);


  {
    map< pair< int, int >, int > conv;
    for(int i = 0; i < bct.bc.size(); i++) {
      for(auto &p : bct.bc[i]) {
        conv[p] = i;
      }
    }
    for(int i = 0; i < M; i++) {
      weight[conv[minmax(A[i], B[i])]] ^= C[i];
    }
    for(int i = 0; i < t.size(); i++) {
      if(i < bct.bc.size() && bct.bc[i].size() >= 2) continue;
      weight[i] = 0;
    }
  }

  using vi = vector< int >;

  auto f = [](vi &a, int b) {
    for(int y : a) chmin(b, b ^ y);
    if(b) a.emplace_back(b);
  };


  int Q;
  cin >> Q;
  vector< int > X(Q), Y(Q), K(Q), Z(Q);
  for(int i = 0; i < Q; i++) {
    cin >> X[i] >> Y[i] >> K[i];
    --X[i], --Y[i], --K[i];
    Z[i] = beet.dist(X[i], Y[i]);
    X[i] = rev[X[i]];
    Y[i] = rev[Y[i]];
  }

  vector< vector< int > > ev(t.size());
  for(int i = 0; i < Q; i++) {
    ev[X[i]].emplace_back(i);
    ev[Y[i]].emplace_back(i);
  }


  vector< int > used(t.size());

  vector< vector< int > > cash(t.size());
  vector< int > last(Q);
  int ptr = 1;
  vector< int > ans(Q);


  auto calc_ans = [&](const vector< int > &a, vector< int > b, int k, int base) {
    for(int x : a) {
      for(int y : b) chmin(x, x ^ y);
      if(x) b.emplace_back(x);
    }
    auto &tap = b;
    if(1 << tap.size() <= k) {
      return -1;
    } else {
      sort(tap.begin(), tap.end());
      for(int j = (int) tap.size() - 1; j >= 0; j--) {
        if(k < (1 << j)) {
          chmin(base, base ^ tap[j]);
        } else {
          k -= 1 << j;
          chmax(base, base ^ tap[j]);
        }
      }
      return base;
    }
  };

  auto add_dfs = MFP([&](auto add_dfs, int idx, int par, vector< int > base, int Left, int id) -> void {
    if(weight[idx]) f(base, weight[idx]);
    cash[idx] = base;

    for(auto &q : ev[idx]) {
      if(Left <= last[q] && last[q] < id) ans[q] = calc_ans(cash[X[q]], cash[Y[q]], K[q], Z[q]);
      last[q] = id;
    }

    for(auto &to : t[idx]) {
      if(to == par) continue;
      if(used[to]) continue;
      add_dfs(to, idx, base, Left, id);
    }
  });


  MFP([&](auto dfs, int centroid) -> void {
    used[centroid] = true;

    vector< int > base;
    int Left = ptr;
    if(weight[centroid]) base.emplace_back(weight[centroid]);
    for(auto &q : ev[centroid]) {
      if(last[q] == ptr) ans[q] = calc_ans(base, base, K[q], Z[q]);
      last[q] = ptr;
    }
    ++ptr;

    for(auto &to : t[centroid]) {
      if(used[to]) continue;
      add_dfs(to, centroid, base, Left, ptr++);
    }

    for(auto &to : ushitapunichia[centroid]) dfs(to);
    used[centroid] = false;
  })(root);


  for(auto &p : ans) cout << p << "\n";
}
accuracy: 0.09523809523809523
time_ms: 680
memory_kb: 133020
final_rank: 19

code_snippet: // merge speed up
#include <bits/stdc++.h>
using namespace std;
using pii = pair<int, int>;
using graph = vector<vector<pii>>;

const int WS = 30;

using basis = array<int, WS>;

const int BN = 18; // larger than log2_(M + 1)

template <typename T>
constexpr T INF = INT_MAX;

template <>
constexpr pii INF<pii> = pii(INT_MAX, INT_MAX);

void add_basis(basis& bs, int x) {
	for (int b = WS - 1; b >= 0 && x != 0; b--) {
		if (x & (1 << b)) {
			if (bs[b]) {
				x ^= bs[b];
			}
			else {
				bs[b] = x;
				break;
			}
		}
	}
}

void merge_basis(basis& bs, const basis& ad) {
	if (count(bs.begin(), bs.end(), 0) == 0) return;
	if (count(ad.begin(), ad.end(), 0) == 0) {
		bs = ad;
		return;
	}
	for (int bb = WS - 1; bb >= 0; bb--) if (ad[bb] != 0) {
		int x = ad[bb];
		for (int b = bb; b >= 0 && x > 0; b--) {
			if (x & (1 << b)) {
				if (bs[b]) {
					x ^= bs[b];
				}
				else {
					bs[b] = x;
					break;
				}
			}
		}
	}
}

void dfs(int v, int prev, const graph& G, vector<pair<vector<int>, int>>& rps, vector<int>& a, vector<int>& cost, vector<int>& tmp) {
	tmp.push_back(v);
	a[v] = prev == -1 ? 0 : a[prev] + 1;
	for (const auto& e : G[v]) if (e.first != prev) {
		if (a[e.first] == -1) {
			cost[e.first] = cost[v] ^ e.second;
			dfs(e.first, v, G, rps, a, cost, tmp);
		}
		else if (a[e.first] < (int)tmp.size() && tmp[a[e.first]] == e.first) {
			rps.emplace_back(vector<int>(tmp.begin() + a[e.first], tmp.end()), cost[v] ^ cost[e.first] ^ e.second);
		}
	}
	tmp.pop_back();
}

vector<pair<vector<int>, int>> enum_roop(const graph& G, vector<int>& cost) {
	const int n = G.size();
	vector<int> a(n, -1), tmp;
	vector<pair<vector<int>, int>> rps;
	dfs(0, -1, G, rps, a, cost, tmp);
	return rps;
}

vector<vector<int>> make_tree(const graph &G, const vector<pair<vector<int>, int>>& rps) {
	const int n = G.size(), s = rps.size();
	vector<set<int>> gs(n + s);
	for (int i = 0; i < n; i++) {
		for (const auto& e : G[i]) {
			gs[i].insert(e.first);
		}
	}
	for (int i = 0; i < s; i++) {
		int v = n + i, x = rps[i].first.size();
		for (int j = 0; j < x; j++) {
			gs[rps[i].first[j]].erase(rps[i].first[(j + 1) % x]);
			gs[rps[i].first[j]].erase(rps[i].first[(j + x - 1) % x]);
			gs[rps[i].first[j]].insert(v);
			gs[v].insert(rps[i].first[j]);
		}
	}
	vector<vector<int>> g;
	for (int i = 0; i < (int)gs.size(); i++) {
		g.emplace_back(gs[i].begin(), gs[i].end());
	}
	return g;
}

struct Monoid {
	using type = basis;
	static type id() {
		basis b;
		for (int i = 0; i < WS; i++) b[i] = 0;
		return b;
	}
	static type op(const type& l, const type & r) {
		auto tmp = l;
		merge_basis(tmp, r);
		return tmp;
	}
};

template <typename M>
class sparse_table {
	using T = typename M::type;
	const int n;
	vector<vector<T>> t;
public:
	sparse_table(const vector<T>& b) : n(b.size()), t(1, b) {
		for (int i = 2, j = 1, p = 0; i <= n; i <<= 1, j++, p = 0) {
			t.emplace_back(n);
			for (int k = 0; k + i <= n; k++)
				t[j][p++] = M::op(t[j - 1][k], t[j - 1][k + (i >> 1)]);
		}
	}
	T find(int l, int r) const { // [l, r)
		assert(0 <= l && l < r && r <= n);
		int i = 31 - __builtin_clz(r - l);
		return M::op(t[i][l], t[i][r - (1 << i)]);
	}
};

class heavy_light_decomposition {
	const int n;
	vector<vector<int>> g;
	vector<int> par, head, in, out;
	void dfs1(int rt) {
		vector<int> size(n, 1);
		vector<size_t> iter(n);
		vector<pair<int, int>> stp;
		stp.reserve(n);
		stp.emplace_back(rt, -1);
		while (!stp.empty()) {
			int v = stp.back().first;
			if (iter[v] < g[v].size()) {
				if (g[v][iter[v]] != stp.back().second) {
					stp.emplace_back(g[v][iter[v]], v);
				}
				++iter[v];
				continue;
			}
			par[v] = stp.back().second;
			for (auto& u : g[v]) if (u == par[v]) {
				if (u != g[v].back()) swap(u, g[v].back());
				g[v].pop_back();
				break;
			}
			for (auto& u : g[v]) {
				size[v] += size[u];
				if (size[u] > size[g[v].front()]) swap(u, g[v].front());
			}
			stp.pop_back();
		}
	}
	void dfs2(int rt) {
		int it = 0;
		vector<size_t> iter(n);
		vector<int> st; st.reserve(n);
		st.push_back(rt);
		while (!st.empty()) {
			int v = st.back();
			if (!iter[v]) in[v] = it++;
			if (iter[v] < g[v].size()) {
				int u = g[v][iter[v]];
				head[u] = iter[v] ? u : head[v];
				st.push_back(u);
				++iter[v];
				continue;
			}
			out[v] = it;
			st.pop_back();
		}
	}
public:
	heavy_light_decomposition(int n_)
		: n(n_), g(n), par(n), head(n), in(n), out(n) {}
	heavy_light_decomposition(const vector<vector<int>>& G)
		: n(G.size()), g(G), par(n), head(n), in(n), out(n) {}
	void add_edge(int u, int v) {
		g[u].push_back(v);
		g[v].push_back(u);
	}
	void build(int rt = 0) {
		dfs1(rt);
		head[rt] = rt;
		dfs2(rt);
	}
	int get_id(int v) {
		return in[v];
	}
	int get_lca(int u, int v) {
		while (true) {
			if (in[u] > in[v]) swap(u, v);
			if (head[u] == head[v]) return u;
			v = par[head[v]];
		}
	}
	void path_query(int u, int v, function<void(int, int)> f) {
		while (true) {
			if (in[u] > in[v]) swap(u, v);
			f(max(in[head[v]], in[u]), in[v] + 1);
			if (head[u] == head[v]) return;
			v = par[head[v]];
		}
	}
	void path_query(int u, int v, function<void(int, int, bool)> f) {
		while (true) {
			if (in[u] <= in[v]) {
				f(max(in[head[v]], in[u]), in[v] + 1, false);
				if (head[u] == head[v]) return;
				v = par[head[v]];
			}
			else {
				f(max(in[head[u]], in[v]), in[u] + 1, true);
				if (head[u] == head[v]) return;
				u = par[head[u]];
			}
		}
	}
	void edge_path_query(int u, int v, function<void(int, int)> f) {
		while (true) {
			if (in[u] > in[v]) swap(u, v);
			if (head[u] != head[v]) {
				f(max(in[head[v]], in[u]), in[v] + 1);
				v = par[head[v]];
			}
			else {
				if (u != v) f(in[u] + 1, in[v] + 1);
				break;
			}
		}
	}
	void edge_path_query(int u, int v, function<void(int, int, bool)> f) {
		while (true) {
			if (in[u] > in[v]) {
				if (head[u] != head[v]) {
					f(max(in[head[u]], in[v]), in[u] + 1, true);
					u = par[head[u]];
				}
				else {
					if (u != v) f(in[v] + 1, in[u] + 1, false);
					break;
				}
			}
			else {
				if (head[u] != head[v]) {
					f(max(in[head[v]], in[u]), in[v] + 1, false);
					v = par[head[v]];
				}
				else {
					if (u != v) f(in[u] + 1, in[v] + 1, false);
					break;
				}
			}
		}
	}
	void subtree_query(int v, function<void(int, int)> f) {
		f(in[v], out[v]);
	}
};

int calc(const basis& bs, int x, int k) {
	int cnt = 0;
	for (int i = 0; i < WS; i++) cnt += bs[i] != 0;
	if (k >= (1 << cnt)) return -1;
	for (int i = WS - 1; i >= 0; i--) if (bs[i] != 0) {
		--cnt;
		if (k < (1 << cnt)) {
			x = min(x, x ^ bs[i]);
		}
		else {
			k -= 1 << cnt;
			x = max(x, x ^ bs[i]);
		}
	}
	return x;
}

int main()
{
	ios::sync_with_stdio(false), cin.tie(0);
	int N, M;
	cin >> N >> M;
	graph G(N);
	for (int i = 0; i < M; i++) {
		int u, v, c;
		cin >> u >> v >> c; --u, --v;
		G[u].emplace_back(v, c);
		G[v].emplace_back(u, c);
	}
	vector<int> cost(N);
	auto rps = enum_roop(G, cost);
	auto g = make_tree(G, rps);
	const int n = N;
	N = g.size();
	assert(N == M + 1);
	heavy_light_decomposition hl(g);
	hl.build();
	vector<Monoid::type> md(N, Monoid::id());
	for (int i = 0; i < N; i++) {
		auto val = Monoid::id();
		int id = hl.get_id(i);
		if (i >= n) add_basis(val, rps[i - n].second);
		md[id] = val;
	}
	sparse_table<Monoid> st(md);
	int Q;
	cin >> Q;
	while (Q--) {
		int x, y, k;
		cin >> x >> y >> k; --x, --y, --k;
		basis bb = Monoid::id();
		hl.path_query(x, y, [&](int u, int v) {
			merge_basis(bb, st.find(u, v));
		});
		printf("%d\n", calc(bb, cost[x] ^ cost[y], k));
	}
	return 0;
}
accuracy: 1
time_ms: 2480
memory_kb: 380964
final_rank: 12

code_snippet: // merge speed up
#include <bits/stdc++.h>
using namespace std;
using pii = pair<int, int>;
using graph = vector<vector<pii>>;

const int WS = 30;

using basis = array<int, WS>;

const int BN = 18; // larger than log2_(M + 1)

template <typename T>
constexpr T INF = INT_MAX;

template <>
constexpr pii INF<pii> = pii(INT_MAX, INT_MAX);

void add_basis(basis& bs, int x) {
	for (int b = WS - 1; b >= 0 && x != 0; b--) {
		if (x & (1 << b)) {
			if (bs[b]) {
				x ^= bs[b];
			}
			else {
				bs[b] = x;
				break;
			}
		}
	}
}

void merge_basis(basis& bs, const basis& ad) {
	if (count(bs.begin(), bs.end(), 0) == 0) return;
	if (count(ad.begin(), ad.end(), 0) == 0) {
		bs = ad;
		return;
	}
	for (int bb = WS - 1; bb >= 0; bb--) if (ad[bb] != 0) {
		int x = ad[bb];
		for (int b = bb; b >= 0 && x > 0; b--) {
			if (x & (1 << b)) {
				if (bs[b]) {
					x ^= bs[b];
				}
				else {
					bs[b] = x;
					break;
				}
			}
		}
	}
}

void dfs(int v, int prev, const graph& G, vector<pair<vector<int>, int>>& rps, vector<int>& a, vector<int>& cost, vector<int>& tmp) {
	tmp.push_back(v);
	a[v] = prev == -1 ? 0 : a[prev] + 1;
	for (const auto& e : G[v]) if (e.first != prev) {
		if (a[e.first] == -1) {
			cost[e.first] = cost[v] ^ e.second;
			dfs(e.first, v, G, rps, a, cost, tmp);
		}
		else if (a[e.first] < (int)tmp.size() && tmp[a[e.first]] == e.first) {
			rps.emplace_back(vector<int>(tmp.begin() + a[e.first], tmp.end()), cost[v] ^ cost[e.first] ^ e.second);
		}
	}
	tmp.pop_back();
}

vector<pair<vector<int>, int>> enum_roop(const graph& G, vector<int>& cost) {
	const int n = G.size();
	vector<int> a(n, -1), tmp;
	vector<pair<vector<int>, int>> rps;
	dfs(0, -1, G, rps, a, cost, tmp);
	return rps;
}

vector<vector<int>> make_tree(const graph &G, const vector<pair<vector<int>, int>>& rps) {
	const int n = G.size(), s = rps.size();
	vector<set<int>> gs(n + s);
	for (int i = 0; i < n; i++) {
		for (const auto& e : G[i]) {
			gs[i].insert(e.first);
		}
	}
	for (int i = 0; i < s; i++) {
		int v = n + i, x = rps[i].first.size();
		for (int j = 0; j < x; j++) {
			gs[rps[i].first[j]].erase(rps[i].first[(j + 1) % x]);
			gs[rps[i].first[j]].erase(rps[i].first[(j + x - 1) % x]);
			gs[rps[i].first[j]].insert(v);
			gs[v].insert(rps[i].first[j]);
		}
	}
	vector<vector<int>> g;
	for (int i = 0; i < (int)gs.size(); i++) {
		g.emplace_back(gs[i].begin(), gs[i].end());
	}
	return g;
}

struct Monoid {
	using type = basis;
	static type id() {
		basis b;
		for (int i = 0; i < WS; i++) b[i] = 0;
		return b;
	}
	static type op(const type& l, const type & r) {
		auto tmp = l;
		merge_basis(tmp, r);
		return tmp;
	}
};

template <typename M>
class segment_tree {
	using T = typename M::type;
	const int n;
	vector<T> data;
	int expand(int x) {
		int res;
		for (res = 1; res < x; res <<= 1);
		return res;
	}
public:
	segment_tree(int n_) : n(expand(n_)), data(n * 2, M::id()) {}
	segment_tree(int n_, T val) : n(expand(n_)), data(n * 2, val) {}
	void init(const vector<T>& data_) {
		for (int i = 0; i < (int)data_.size(); i++)
			data[i + n] = data_[i];
		for (int i = n - 1; i >= 0; i--)
			data[i] = M::op(data[i * 2], data[i * 2 + 1]);
	}
	void update(int p, T val) {
		data[p += n] = val;
		while (p >>= 1) data[p] = M::op(data[p * 2], data[p * 2 + 1]);
	}
	T find(int l, int r) {
		l += n; r += n;
		T res1 = M::id(), res2 = M::id();
		while (l < r) {
			if (l & 1) res1 = M::op(res1, data[l++]);
			if (r & 1) res2 = M::op(data[--r], res2);
			l >>= 1; r >>= 1;
		}
		return M::op(res1, res2);
	}
};

class heavy_light_decomposition {
	const int n;
	vector<vector<int>> g;
	vector<int> par, head, in, out;
	void dfs1(int rt) {
		vector<int> size(n, 1);
		vector<size_t> iter(n);
		vector<pair<int, int>> stp;
		stp.reserve(n);
		stp.emplace_back(rt, -1);
		while (!stp.empty()) {
			int v = stp.back().first;
			if (iter[v] < g[v].size()) {
				if (g[v][iter[v]] != stp.back().second) {
					stp.emplace_back(g[v][iter[v]], v);
				}
				++iter[v];
				continue;
			}
			par[v] = stp.back().second;
			for (auto& u : g[v]) if (u == par[v]) {
				if (u != g[v].back()) swap(u, g[v].back());
				g[v].pop_back();
				break;
			}
			for (auto& u : g[v]) {
				size[v] += size[u];
				if (size[u] > size[g[v].front()]) swap(u, g[v].front());
			}
			stp.pop_back();
		}
	}
	void dfs2(int rt) {
		int it = 0;
		vector<size_t> iter(n);
		vector<int> st; st.reserve(n);
		st.push_back(rt);
		while (!st.empty()) {
			int v = st.back();
			if (!iter[v]) in[v] = it++;
			if (iter[v] < g[v].size()) {
				int u = g[v][iter[v]];
				head[u] = iter[v] ? u : head[v];
				st.push_back(u);
				++iter[v];
				continue;
			}
			out[v] = it;
			st.pop_back();
		}
	}
public:
	heavy_light_decomposition(int n_)
		: n(n_), g(n), par(n), head(n), in(n), out(n) {}
	heavy_light_decomposition(const vector<vector<int>>& G)
		: n(G.size()), g(G), par(n), head(n), in(n), out(n) {}
	void add_edge(int u, int v) {
		g[u].push_back(v);
		g[v].push_back(u);
	}
	void build(int rt = 0) {
		dfs1(rt);
		head[rt] = rt;
		dfs2(rt);
	}
	int get_id(int v) {
		return in[v];
	}
	int get_lca(int u, int v) {
		while (true) {
			if (in[u] > in[v]) swap(u, v);
			if (head[u] == head[v]) return u;
			v = par[head[v]];
		}
	}
	void path_query(int u, int v, function<void(int, int)> f) {
		while (true) {
			if (in[u] > in[v]) swap(u, v);
			f(max(in[head[v]], in[u]), in[v] + 1);
			if (head[u] == head[v]) return;
			v = par[head[v]];
		}
	}
	void path_query(int u, int v, function<void(int, int, bool)> f) {
		while (true) {
			if (in[u] <= in[v]) {
				f(max(in[head[v]], in[u]), in[v] + 1, false);
				if (head[u] == head[v]) return;
				v = par[head[v]];
			}
			else {
				f(max(in[head[u]], in[v]), in[u] + 1, true);
				if (head[u] == head[v]) return;
				u = par[head[u]];
			}
		}
	}
	void edge_path_query(int u, int v, function<void(int, int)> f) {
		while (true) {
			if (in[u] > in[v]) swap(u, v);
			if (head[u] != head[v]) {
				f(max(in[head[v]], in[u]), in[v] + 1);
				v = par[head[v]];
			}
			else {
				if (u != v) f(in[u] + 1, in[v] + 1);
				break;
			}
		}
	}
	void edge_path_query(int u, int v, function<void(int, int, bool)> f) {
		while (true) {
			if (in[u] > in[v]) {
				if (head[u] != head[v]) {
					f(max(in[head[u]], in[v]), in[u] + 1, true);
					u = par[head[u]];
				}
				else {
					if (u != v) f(in[v] + 1, in[u] + 1, false);
					break;
				}
			}
			else {
				if (head[u] != head[v]) {
					f(max(in[head[v]], in[u]), in[v] + 1, false);
					v = par[head[v]];
				}
				else {
					if (u != v) f(in[u] + 1, in[v] + 1, false);
					break;
				}
			}
		}
	}
	void subtree_query(int v, function<void(int, int)> f) {
		f(in[v], out[v]);
	}
};

int calc(const basis& bs, int x, int k) {
	int cnt = 0;
	for (int i = 0; i < WS; i++) cnt += bs[i] != 0;
	if (k >= (1 << cnt)) return -1;
	for (int i = WS - 1; i >= 0; i--) if (bs[i] != 0) {
		--cnt;
		if (k < (1 << cnt)) {
			x = min(x, x ^ bs[i]);
		}
		else {
			k -= 1 << cnt;
			x = max(x, x ^ bs[i]);
		}
	}
	return x;
}

int main()
{
	ios::sync_with_stdio(false), cin.tie(0);
	int N, M;
	cin >> N >> M;
	graph G(N);
	for (int i = 0; i < M; i++) {
		int u, v, c;
		cin >> u >> v >> c; --u, --v;
		G[u].emplace_back(v, c);
		G[v].emplace_back(u, c);
	}
	vector<int> cost(N);
	auto rps = enum_roop(G, cost);
	auto g = make_tree(G, rps);
	const int n = N;
	N = g.size();
	assert(N == M + 1);
	heavy_light_decomposition hl(g);
	hl.build();
	vector<Monoid::type> md(N, Monoid::id());
	for (int i = 0; i < N; i++) {
		auto val = Monoid::id();
		int id = hl.get_id(i);
		if (i >= n) add_basis(val, rps[i - n].second);
		md[id] = val;
	}
	segment_tree<Monoid> st(N);
	st.init(md);
	int Q;
	cin >> Q;
	while (Q--) {
		int x, y, k;
		cin >> x >> y >> k; --x, --y, --k;
		basis bb = Monoid::id();
		hl.path_query(x, y, [&](int u, int v) {
			merge_basis(bb, st.find(u, v));
		});
		printf("%d\n", calc(bb, cost[x] ^ cost[y], k));
	}
	return 0;
}
accuracy: 1
time_ms: 2610
memory_kb: 126100
final_rank: 9

code_snippet: // merge speed up
#include <bits/stdc++.h>
using namespace std;
using pii = pair<int, int>;
using graph = vector<vector<pii>>;


const int WS = 30;

using basis = array<int, WS>;

const int BN = 18; // larger than log2_(M + 1)

template <typename T>
constexpr T INF = INT_MAX;

template <>
constexpr pii INF<pii> = pii(INT_MAX, INT_MAX);

void add_basis(basis& bs, int x) {
	for (int b = WS - 1; b >= 0 && x != 0; b--) {
		if (x & (1 << b)) {
			if (bs[b]) {
				x ^= bs[b];
			}
			else {
				bs[b] = x;
				break;
			}
		}
	}
}

void merge_basis(basis& bs, const basis& ad) {
	if (count(bs.begin(), bs.end(), 0) == 0) return;
	if (count(ad.begin(), ad.end(), 0) == 0) {
		bs = ad;
		return;
	}
	for (int bb = WS - 1; bb >= 0; bb--) if (ad[bb] != 0) {
		int x = ad[bb];
		for (int b = bb; b >= 0 && x > 0; b--) {
			if (x & (1 << b)) {
				if (bs[b]) {
					x ^= bs[b];
				}
				else {
					bs[b] = x;
					break;
				}
			}
		}
	}
}

void dfs(int v, int prev, const graph& G, vector<pair<vector<int>, int>>& rps, vector<int>& a, vector<int>& cost, vector<int>& tmp) {
	tmp.push_back(v);
	a[v] = prev == -1 ? 0 : a[prev] + 1;
	for (const auto& e : G[v]) if (e.first != prev) {
		if (a[e.first] == -1) {
			cost[e.first] = cost[v] ^ e.second;
			dfs(e.first, v, G, rps, a, cost, tmp);
		}
		else if (a[e.first] < (int)tmp.size() && tmp[a[e.first]] == e.first) {
			rps.emplace_back(vector<int>(tmp.begin() + a[e.first], tmp.end()), cost[v] ^ cost[e.first] ^ e.second);
		}
	}
	tmp.pop_back();
}

vector<pair<vector<int>, int>> enum_roop(const graph& G, vector<int>& cost) {
	const int n = G.size();
	vector<int> a(n, -1), tmp;
	vector<pair<vector<int>, int>> rps;
	dfs(0, -1, G, rps, a, cost, tmp);
	return rps;
}

vector<vector<int>> make_tree(const graph &G, const vector<pair<vector<int>, int>>& rps) {
	const int n = G.size(), s = rps.size();
	vector<set<int>> gs(n + s);
	for (int i = 0; i < n; i++) {
		for (const auto& e : G[i]) {
			gs[i].insert(e.first);
		}
	}
	for (int i = 0; i < s; i++) {
		int v = n + i, x = rps[i].first.size();
		for (int j = 0; j < x; j++) {
			gs[rps[i].first[j]].erase(rps[i].first[(j + 1) % x]);
			gs[rps[i].first[j]].erase(rps[i].first[(j + x - 1) % x]);
			gs[rps[i].first[j]].insert(v);
			gs[v].insert(rps[i].first[j]);
		}
	}
	vector<vector<int>> g;
	for (int i = 0; i < (int)gs.size(); i++) {
		g.emplace_back(gs[i].begin(), gs[i].end());
	}
	return g;
}

struct Monoid {
	using type = basis;
	static type id() {
		basis b;
		for (int i = 0; i < WS; i++) b[i] = 0;
		return b;
	}
	static type op(const type& l, const type & r) {
		auto tmp = l;
		merge_basis(tmp, r);
		return tmp;
	}
};

template <typename M>
class sparse_table {
	using T = typename M::type;
	const int n;
	vector<vector<T>> t;
public:
	sparse_table(const vector<T>& b) : n(b.size()), t(1, b) {
		for (int i = 2, j = 1, p = 0; i <= n; i <<= 1, j++, p = 0) {
			t.emplace_back(n);
			for (int k = 0; k + i <= n; k++)
				t[j][p++] = M::op(t[j - 1][k], t[j - 1][k + (i >> 1)]);
		}
	}
	T find(int l, int r) const { // [l, r)
		assert(0 <= l && l < r && r <= n);
		int i = 31 - __builtin_clz(r - l);
		return M::op(t[i][l], t[i][r - (1 << i)]);
	}
};

class heavy_light_decomposition {
	const int n;
	vector<vector<int>> g;
	vector<int> par, head, in, out;
	void dfs1(int rt) {
		vector<int> size(n, 1);
		vector<size_t> iter(n);
		vector<pair<int, int>> stp;
		stp.reserve(n);
		stp.emplace_back(rt, -1);
		while (!stp.empty()) {
			int v = stp.back().first;
			if (iter[v] < g[v].size()) {
				if (g[v][iter[v]] != stp.back().second) {
					stp.emplace_back(g[v][iter[v]], v);
				}
				++iter[v];
				continue;
			}
			par[v] = stp.back().second;
			for (auto& u : g[v]) if (u == par[v]) {
				if (u != g[v].back()) swap(u, g[v].back());
				g[v].pop_back();
				break;
			}
			for (auto& u : g[v]) {
				size[v] += size[u];
				if (size[u] > size[g[v].front()]) swap(u, g[v].front());
			}
			stp.pop_back();
		}
	}
	void dfs2(int rt) {
		int it = 0;
		vector<size_t> iter(n);
		vector<int> st; st.reserve(n);
		st.push_back(rt);
		while (!st.empty()) {
			int v = st.back();
			if (!iter[v]) in[v] = it++;
			if (iter[v] < g[v].size()) {
				int u = g[v][iter[v]];
				head[u] = iter[v] ? u : head[v];
				st.push_back(u);
				++iter[v];
				continue;
			}
			out[v] = it;
			st.pop_back();
		}
	}
public:
	heavy_light_decomposition(int n_)
		: n(n_), g(n), par(n), head(n), in(n), out(n) {}
	heavy_light_decomposition(const vector<vector<int>>& G)
		: n(G.size()), g(G), par(n), head(n), in(n), out(n) {}
	void add_edge(int u, int v) {
		g[u].push_back(v);
		g[v].push_back(u);
	}
	void build(int rt = 0) {
		dfs1(rt);
		head[rt] = rt;
		dfs2(rt);
	}
	int get_id(int v) {
		return in[v];
	}
	int get_lca(int u, int v) {
		while (true) {
			if (in[u] > in[v]) swap(u, v);
			if (head[u] == head[v]) return u;
			v = par[head[v]];
		}
	}
	void path_query(int u, int v, function<void(int, int)> f) {
		while (true) {
			if (in[u] > in[v]) swap(u, v);
			f(max(in[head[v]], in[u]), in[v] + 1);
			if (head[u] == head[v]) return;
			v = par[head[v]];
		}
	}
	void path_query(int u, int v, function<void(int, int, bool)> f) {
		while (true) {
			if (in[u] <= in[v]) {
				f(max(in[head[v]], in[u]), in[v] + 1, false);
				if (head[u] == head[v]) return;
				v = par[head[v]];
			}
			else {
				f(max(in[head[u]], in[v]), in[u] + 1, true);
				if (head[u] == head[v]) return;
				u = par[head[u]];
			}
		}
	}
	void edge_path_query(int u, int v, function<void(int, int)> f) {
		while (true) {
			if (in[u] > in[v]) swap(u, v);
			if (head[u] != head[v]) {
				f(max(in[head[v]], in[u]), in[v] + 1);
				v = par[head[v]];
			}
			else {
				if (u != v) f(in[u] + 1, in[v] + 1);
				break;
			}
		}
	}
	void edge_path_query(int u, int v, function<void(int, int, bool)> f) {
		while (true) {
			if (in[u] > in[v]) {
				if (head[u] != head[v]) {
					f(max(in[head[u]], in[v]), in[u] + 1, true);
					u = par[head[u]];
				}
				else {
					if (u != v) f(in[v] + 1, in[u] + 1, false);
					break;
				}
			}
			else {
				if (head[u] != head[v]) {
					f(max(in[head[v]], in[u]), in[v] + 1, false);
					v = par[head[v]];
				}
				else {
					if (u != v) f(in[u] + 1, in[v] + 1, false);
					break;
				}
			}
		}
	}
	void subtree_query(int v, function<void(int, int)> f) {
		f(in[v], out[v]);
	}
};

int calc(const basis& bs, int x, int k) {
	int cnt = 0;
	for (int i = 0; i < WS; i++) cnt += bs[i] != 0;
	if (k >= (1 << cnt)) return -1;
	for (int i = WS - 1; i >= 0; i--) if (bs[i] != 0) {
		--cnt;
		if (k < (1 << cnt)) {
			x = min(x, x ^ bs[i]);
		}
		else {
			k -= 1 << cnt;
			x = max(x, x ^ bs[i]);
		}
	}
	return x;
}

int main()
{
	ios::sync_with_stdio(false), cin.tie(0);
	int N, M;
	cin >> N >> M;
	graph G(N);
	for (int i = 0; i < M; i++) {
		int u, v, c;
		cin >> u >> v >> c; --u, --v;
		G[u].emplace_back(v, c);
		G[v].emplace_back(u, c);
	}
	vector<int> cost(N);
	auto rps = enum_roop(G, cost);
	auto g = make_tree(G, rps);
	const int n = N;
	N = g.size();
	assert(N == M + 1);
	heavy_light_decomposition hl(g);
	hl.build();
	vector<Monoid::type> md(N, Monoid::id());
	for (int i = 0; i < N; i++) {
		auto val = Monoid::id();
		int id = hl.get_id(i);
		if (i >= n) add_basis(val, rps[i - n].second);
		md[id] = val;
	}
	sparse_table<Monoid> st(md);
	int Q;
	cin >> Q;
	while (Q--) {
		int x, y, k;
		cin >> x >> y >> k; --x, --y, --k;
		basis bb = Monoid::id();
		hl.path_query(x, y, [&](int u, int v) {
			merge_basis(bb, st.find(u, v));
		});
		printf("%d\n", calc(bb, cost[x] ^ cost[y], k));
	}
	return 0;
}
accuracy: 1
time_ms: 1510
memory_kb: 381120
final_rank: 10

code_snippet: // merge speed up
#include <bits/stdc++.h>
using namespace std;
using pii = pair<int, int>;
using graph = vector<vector<pii>>;


const int WS = 30;

using basis = array<int, WS>;

const int BN = 18; // larger than log2_(M + 1)

template <typename T>
constexpr T INF = INT_MAX;

template <>
constexpr pii INF<pii> = pii(INT_MAX, INT_MAX);

void add_basis(basis& bs, int x) {
	for (int b = WS - 1; b >= 0 && x != 0; b--) {
		if (x & (1 << b)) {
			if (bs[b]) {
				x ^= bs[b];
			}
			else {
				bs[b] = x;
				break;
			}
		}
	}
}

void merge_basis(basis& bs, const basis& ad) {
	bool ng = false;
	for (int b = 0; b + 1 < WS; b++) {
		if (bs[b] == 0 && bs[b + 1] != 0) {
			ng = true;
			break;
		}
		if (ad[b] == 0 && ad[b + 1] != 0) {
			ng = true;
			break;
		}
	}
	if (!ng) {
		int c1 = 0, c2 = 0;
		for (int b = 0; b < WS; b++) {
			if (bs[b] != 0) {
				c1 = b;
			}
			if (ad[b] != 0) {
				c2 = b;
			}
		}
		if (c1 < c2) bs = ad;
		return;
	}
	for (int bb = WS - 1; bb >= 0; bb--) if (ad[bb] != 0) {
		int x = ad[bb];
		for (int b = bb; b >= 0 && x > 0; b--) {
			if (x & (1 << b)) {
				if (bs[b]) {
					x ^= bs[b];
				}
				else {
					bs[b] = x;
					break;
				}
			}
		}
	}
}

void dfs(int v, int prev, const graph& G, vector<pair<vector<int>, int>>& rps, vector<int>& a, vector<int>& cost, vector<int>& tmp) {
	tmp.push_back(v);
	a[v] = prev == -1 ? 0 : a[prev] + 1;
	for (const auto& e : G[v]) if (e.first != prev) {
		if (a[e.first] == -1) {
			cost[e.first] = cost[v] ^ e.second;
			dfs(e.first, v, G, rps, a, cost, tmp);
		}
		else if (a[e.first] < (int)tmp.size() && tmp[a[e.first]] == e.first) {
			rps.emplace_back(vector<int>(tmp.begin() + a[e.first], tmp.end()), cost[v] ^ cost[e.first] ^ e.second);
		}
	}
	tmp.pop_back();
}

vector<pair<vector<int>, int>> enum_roop(const graph& G, vector<int>& cost) {
	const int n = G.size();
	vector<int> a(n, -1), tmp;
	vector<pair<vector<int>, int>> rps;
	dfs(0, -1, G, rps, a, cost, tmp);
	return rps;
}

vector<vector<int>> make_tree(const graph &G, const vector<pair<vector<int>, int>>& rps) {
	const int n = G.size(), s = rps.size();
	vector<set<int>> gs(n + s);
	for (int i = 0; i < n; i++) {
		for (const auto& e : G[i]) {
			gs[i].insert(e.first);
		}
	}
	for (int i = 0; i < s; i++) {
		int v = n + i, x = rps[i].first.size();
		for (int j = 0; j < x; j++) {
			gs[rps[i].first[j]].erase(rps[i].first[(j + 1) % x]);
			gs[rps[i].first[j]].erase(rps[i].first[(j + x - 1) % x]);
			gs[rps[i].first[j]].insert(v);
			gs[v].insert(rps[i].first[j]);
		}
	}
	vector<vector<int>> g;
	for (int i = 0; i < (int)gs.size(); i++) {
		g.emplace_back(gs[i].begin(), gs[i].end());
	}
	return g;
}

struct Monoid {
	using type = basis;
	static type id() {
		basis b;
		for (int i = 0; i < WS; i++) b[i] = 0;
		return b;
	}
	static type op(const type& l, const type & r) {
		auto tmp = l;
		merge_basis(tmp, r);
		return tmp;
	}
};

template <typename M>
class sparse_table {
	using T = typename M::type;
	const int n;
	vector<vector<T>> t;
public:
	sparse_table(const vector<T>& b) : n(b.size()), t(1, b) {
		for (int i = 2, j = 1, p = 0; i <= n; i <<= 1, j++, p = 0) {
			t.emplace_back(n);
			for (int k = 0; k + i <= n; k++)
				t[j][p++] = M::op(t[j - 1][k], t[j - 1][k + (i >> 1)]);
		}
	}
	T find(int l, int r) const { // [l, r)
		assert(0 <= l && l < r && r <= n);
		int i = 31 - __builtin_clz(r - l);
		return M::op(t[i][l], t[i][r - (1 << i)]);
	}
};

class heavy_light_decomposition {
	const int n;
	vector<vector<int>> g;
	vector<int> par, head, in, out;
	void dfs1(int rt) {
		vector<int> size(n, 1);
		vector<size_t> iter(n);
		vector<pair<int, int>> stp;
		stp.reserve(n);
		stp.emplace_back(rt, -1);
		while (!stp.empty()) {
			int v = stp.back().first;
			if (iter[v] < g[v].size()) {
				if (g[v][iter[v]] != stp.back().second) {
					stp.emplace_back(g[v][iter[v]], v);
				}
				++iter[v];
				continue;
			}
			par[v] = stp.back().second;
			for (auto& u : g[v]) if (u == par[v]) {
				if (u != g[v].back()) swap(u, g[v].back());
				g[v].pop_back();
				break;
			}
			for (auto& u : g[v]) {
				size[v] += size[u];
				if (size[u] > size[g[v].front()]) swap(u, g[v].front());
			}
			stp.pop_back();
		}
	}
	void dfs2(int rt) {
		int it = 0;
		vector<size_t> iter(n);
		vector<int> st; st.reserve(n);
		st.push_back(rt);
		while (!st.empty()) {
			int v = st.back();
			if (!iter[v]) in[v] = it++;
			if (iter[v] < g[v].size()) {
				int u = g[v][iter[v]];
				head[u] = iter[v] ? u : head[v];
				st.push_back(u);
				++iter[v];
				continue;
			}
			out[v] = it;
			st.pop_back();
		}
	}
public:
	heavy_light_decomposition(int n_)
		: n(n_), g(n), par(n), head(n), in(n), out(n) {}
	heavy_light_decomposition(const vector<vector<int>>& G)
		: n(G.size()), g(G), par(n), head(n), in(n), out(n) {}
	void add_edge(int u, int v) {
		g[u].push_back(v);
		g[v].push_back(u);
	}
	void build(int rt = 0) {
		dfs1(rt);
		head[rt] = rt;
		dfs2(rt);
	}
	int get_id(int v) {
		return in[v];
	}
	int get_lca(int u, int v) {
		while (true) {
			if (in[u] > in[v]) swap(u, v);
			if (head[u] == head[v]) return u;
			v = par[head[v]];
		}
	}
	void path_query(int u, int v, function<void(int, int)> f) {
		while (true) {
			if (in[u] > in[v]) swap(u, v);
			f(max(in[head[v]], in[u]), in[v] + 1);
			if (head[u] == head[v]) return;
			v = par[head[v]];
		}
	}
	void path_query(int u, int v, function<void(int, int, bool)> f) {
		while (true) {
			if (in[u] <= in[v]) {
				f(max(in[head[v]], in[u]), in[v] + 1, false);
				if (head[u] == head[v]) return;
				v = par[head[v]];
			}
			else {
				f(max(in[head[u]], in[v]), in[u] + 1, true);
				if (head[u] == head[v]) return;
				u = par[head[u]];
			}
		}
	}
	void edge_path_query(int u, int v, function<void(int, int)> f) {
		while (true) {
			if (in[u] > in[v]) swap(u, v);
			if (head[u] != head[v]) {
				f(max(in[head[v]], in[u]), in[v] + 1);
				v = par[head[v]];
			}
			else {
				if (u != v) f(in[u] + 1, in[v] + 1);
				break;
			}
		}
	}
	void edge_path_query(int u, int v, function<void(int, int, bool)> f) {
		while (true) {
			if (in[u] > in[v]) {
				if (head[u] != head[v]) {
					f(max(in[head[u]], in[v]), in[u] + 1, true);
					u = par[head[u]];
				}
				else {
					if (u != v) f(in[v] + 1, in[u] + 1, false);
					break;
				}
			}
			else {
				if (head[u] != head[v]) {
					f(max(in[head[v]], in[u]), in[v] + 1, false);
					v = par[head[v]];
				}
				else {
					if (u != v) f(in[u] + 1, in[v] + 1, false);
					break;
				}
			}
		}
	}
	void subtree_query(int v, function<void(int, int)> f) {
		f(in[v], out[v]);
	}
};

int calc(const basis& bs, int x, int k) {
	int cnt = 0;
	for (int i = 0; i < WS; i++) cnt += bs[i] != 0;
	if (k >= (1 << cnt)) return -1;
	for (int i = WS - 1; i >= 0; i--) if (bs[i] != 0) {
		--cnt;
		if (k < (1 << cnt)) {
			x = min(x, x ^ bs[i]);
		}
		else {
			k -= 1 << cnt;
			x = max(x, x ^ bs[i]);
		}
	}
	return x;
}

int main()
{
	ios::sync_with_stdio(false), cin.tie(0);
	int N, M;
	cin >> N >> M;
	graph G(N);
	for (int i = 0; i < M; i++) {
		int u, v, c;
		cin >> u >> v >> c; --u, --v;
		G[u].emplace_back(v, c);
		G[v].emplace_back(u, c);
	}
	vector<int> cost(N);
	auto rps = enum_roop(G, cost);
	auto g = make_tree(G, rps);
	const int n = N;
	N = g.size();
	assert(N == M + 1);
	heavy_light_decomposition hl(g);
	hl.build();
	vector<Monoid::type> md(N, Monoid::id());
	for (int i = 0; i < N; i++) {
		auto val = Monoid::id();
		int id = hl.get_id(i);
		if (i >= n) add_basis(val, rps[i - n].second);
		md[id] = val;
	}
	sparse_table<Monoid> st(md);
	int Q;
	cin >> Q;
	while (Q--) {
		int x, y, k;
		cin >> x >> y >> k; --x, --y, --k;
		basis bb = Monoid::id();
		hl.path_query(x, y, [&](int u, int v) {
			merge_basis(bb, st.find(u, v));
		});
		printf("%d\n", calc(bb, cost[x] ^ cost[y], k));
	}
	return 0;
}
accuracy: 0.14285714285714285
time_ms: 410
memory_kb: 252576
final_rank: 17

code_snippet: // merge speed up
#include <bits/stdc++.h>
using namespace std;
using pii = pair<int, int>;
using graph = vector<vector<pii>>;


const int WS = 30;

using basis = array<int, WS>;

const int BN = 18; // larger than log2_(M + 1)

template <typename T>
constexpr T INF = INT_MAX;

template <>
constexpr pii INF<pii> = pii(INT_MAX, INT_MAX);

void add_basis(basis& bs, int x) {
	for (int b = WS - 1; b >= 0 && x != 0; b--) {
		if (x & (1 << b)) {
			if (bs[b]) {
				x ^= bs[b];
			}
			else {
				bs[b] = x;
				break;
			}
		}
	}
}

void merge_basis(basis& bs, const basis& ad) {
	bool ng = false;
	for (int b = 0; b + 1 < WS; b++) {
		if (bs[b] != 0 && bs[b + 1] == 0) {
			ng = true;
			break;
		}
		if (ad[b] != 0 && ad[b + 1] == 0) {
			ng = true;
			break;
		}
	}
	if (!ng) {
		int c1 = 0, c2 = 0;
		for (int b = 0; b < WS; b++) {
			if (bs[b] != 0) {
				c1 = b;
			}
			if (ad[b] != 0) {
				c2 = b;
			}
		}
		if (c1 < c2) bs = ad;
		return;
	}
	for (int bb = WS - 1; bb >= 0; bb--) if (ad[bb] != 0) {
		int x = ad[bb];
		for (int b = bb; b >= 0 && x > 0; b--) {
			if (x & (1 << b)) {
				if (bs[b]) {
					x ^= bs[b];
				}
				else {
					bs[b] = x;
					break;
				}
			}
		}
	}
}

void dfs(int v, int prev, const graph& G, vector<pair<vector<int>, int>>& rps, vector<int>& a, vector<int>& cost, vector<int>& tmp) {
	tmp.push_back(v);
	a[v] = prev == -1 ? 0 : a[prev] + 1;
	for (const auto& e : G[v]) if (e.first != prev) {
		if (a[e.first] == -1) {
			cost[e.first] = cost[v] ^ e.second;
			dfs(e.first, v, G, rps, a, cost, tmp);
		}
		else if (a[e.first] < (int)tmp.size() && tmp[a[e.first]] == e.first) {
			rps.emplace_back(vector<int>(tmp.begin() + a[e.first], tmp.end()), cost[v] ^ cost[e.first] ^ e.second);
		}
	}
	tmp.pop_back();
}

vector<pair<vector<int>, int>> enum_roop(const graph& G, vector<int>& cost) {
	const int n = G.size();
	vector<int> a(n, -1), tmp;
	vector<pair<vector<int>, int>> rps;
	dfs(0, -1, G, rps, a, cost, tmp);
	return rps;
}

vector<vector<int>> make_tree(const graph &G, const vector<pair<vector<int>, int>>& rps) {
	const int n = G.size(), s = rps.size();
	vector<set<int>> gs(n + s);
	for (int i = 0; i < n; i++) {
		for (const auto& e : G[i]) {
			gs[i].insert(e.first);
		}
	}
	for (int i = 0; i < s; i++) {
		int v = n + i, x = rps[i].first.size();
		for (int j = 0; j < x; j++) {
			gs[rps[i].first[j]].erase(rps[i].first[(j + 1) % x]);
			gs[rps[i].first[j]].erase(rps[i].first[(j + x - 1) % x]);
			gs[rps[i].first[j]].insert(v);
			gs[v].insert(rps[i].first[j]);
		}
	}
	vector<vector<int>> g;
	for (int i = 0; i < (int)gs.size(); i++) {
		g.emplace_back(gs[i].begin(), gs[i].end());
	}
	return g;
}

struct Monoid {
	using type = basis;
	static type id() {
		basis b;
		for (int i = 0; i < WS; i++) b[i] = 0;
		return b;
	}
	static type op(const type& l, const type & r) {
		auto tmp = l;
		merge_basis(tmp, r);
		return tmp;
	}
};

template <typename M>
class sparse_table {
	using T = typename M::type;
	const int n;
	vector<vector<T>> t;
public:
	sparse_table(const vector<T>& b) : n(b.size()), t(1, b) {
		for (int i = 2, j = 1, p = 0; i <= n; i <<= 1, j++, p = 0) {
			t.emplace_back(n);
			for (int k = 0; k + i <= n; k++)
				t[j][p++] = M::op(t[j - 1][k], t[j - 1][k + (i >> 1)]);
		}
	}
	T find(int l, int r) const { // [l, r)
		assert(0 <= l && l < r && r <= n);
		int i = 31 - __builtin_clz(r - l);
		return M::op(t[i][l], t[i][r - (1 << i)]);
	}
};

class heavy_light_decomposition {
	const int n;
	vector<vector<int>> g;
	vector<int> par, head, in, out;
	void dfs1(int rt) {
		vector<int> size(n, 1);
		vector<size_t> iter(n);
		vector<pair<int, int>> stp;
		stp.reserve(n);
		stp.emplace_back(rt, -1);
		while (!stp.empty()) {
			int v = stp.back().first;
			if (iter[v] < g[v].size()) {
				if (g[v][iter[v]] != stp.back().second) {
					stp.emplace_back(g[v][iter[v]], v);
				}
				++iter[v];
				continue;
			}
			par[v] = stp.back().second;
			for (auto& u : g[v]) if (u == par[v]) {
				if (u != g[v].back()) swap(u, g[v].back());
				g[v].pop_back();
				break;
			}
			for (auto& u : g[v]) {
				size[v] += size[u];
				if (size[u] > size[g[v].front()]) swap(u, g[v].front());
			}
			stp.pop_back();
		}
	}
	void dfs2(int rt) {
		int it = 0;
		vector<size_t> iter(n);
		vector<int> st; st.reserve(n);
		st.push_back(rt);
		while (!st.empty()) {
			int v = st.back();
			if (!iter[v]) in[v] = it++;
			if (iter[v] < g[v].size()) {
				int u = g[v][iter[v]];
				head[u] = iter[v] ? u : head[v];
				st.push_back(u);
				++iter[v];
				continue;
			}
			out[v] = it;
			st.pop_back();
		}
	}
public:
	heavy_light_decomposition(int n_)
		: n(n_), g(n), par(n), head(n), in(n), out(n) {}
	heavy_light_decomposition(const vector<vector<int>>& G)
		: n(G.size()), g(G), par(n), head(n), in(n), out(n) {}
	void add_edge(int u, int v) {
		g[u].push_back(v);
		g[v].push_back(u);
	}
	void build(int rt = 0) {
		dfs1(rt);
		head[rt] = rt;
		dfs2(rt);
	}
	int get_id(int v) {
		return in[v];
	}
	int get_lca(int u, int v) {
		while (true) {
			if (in[u] > in[v]) swap(u, v);
			if (head[u] == head[v]) return u;
			v = par[head[v]];
		}
	}
	void path_query(int u, int v, function<void(int, int)> f) {
		while (true) {
			if (in[u] > in[v]) swap(u, v);
			f(max(in[head[v]], in[u]), in[v] + 1);
			if (head[u] == head[v]) return;
			v = par[head[v]];
		}
	}
	void path_query(int u, int v, function<void(int, int, bool)> f) {
		while (true) {
			if (in[u] <= in[v]) {
				f(max(in[head[v]], in[u]), in[v] + 1, false);
				if (head[u] == head[v]) return;
				v = par[head[v]];
			}
			else {
				f(max(in[head[u]], in[v]), in[u] + 1, true);
				if (head[u] == head[v]) return;
				u = par[head[u]];
			}
		}
	}
	void edge_path_query(int u, int v, function<void(int, int)> f) {
		while (true) {
			if (in[u] > in[v]) swap(u, v);
			if (head[u] != head[v]) {
				f(max(in[head[v]], in[u]), in[v] + 1);
				v = par[head[v]];
			}
			else {
				if (u != v) f(in[u] + 1, in[v] + 1);
				break;
			}
		}
	}
	void edge_path_query(int u, int v, function<void(int, int, bool)> f) {
		while (true) {
			if (in[u] > in[v]) {
				if (head[u] != head[v]) {
					f(max(in[head[u]], in[v]), in[u] + 1, true);
					u = par[head[u]];
				}
				else {
					if (u != v) f(in[v] + 1, in[u] + 1, false);
					break;
				}
			}
			else {
				if (head[u] != head[v]) {
					f(max(in[head[v]], in[u]), in[v] + 1, false);
					v = par[head[v]];
				}
				else {
					if (u != v) f(in[u] + 1, in[v] + 1, false);
					break;
				}
			}
		}
	}
	void subtree_query(int v, function<void(int, int)> f) {
		f(in[v], out[v]);
	}
};

int calc(const basis& bs, int x, int k) {
	int cnt = 0;
	for (int i = 0; i < WS; i++) cnt += bs[i] != 0;
	if (k >= (1 << cnt)) return -1;
	for (int i = WS - 1; i >= 0; i--) if (bs[i] != 0) {
		--cnt;
		if (k < (1 << cnt)) {
			x = min(x, x ^ bs[i]);
		}
		else {
			k -= 1 << cnt;
			x = max(x, x ^ bs[i]);
		}
	}
	return x;
}

int main()
{
	ios::sync_with_stdio(false), cin.tie(0);
	int N, M;
	cin >> N >> M;
	graph G(N);
	for (int i = 0; i < M; i++) {
		int u, v, c;
		cin >> u >> v >> c; --u, --v;
		G[u].emplace_back(v, c);
		G[v].emplace_back(u, c);
	}
	vector<int> cost(N);
	auto rps = enum_roop(G, cost);
	auto g = make_tree(G, rps);
	const int n = N;
	N = g.size();
	assert(N == M + 1);
	heavy_light_decomposition hl(g);
	hl.build();
	vector<Monoid::type> md(N, Monoid::id());
	for (int i = 0; i < N; i++) {
		auto val = Monoid::id();
		int id = hl.get_id(i);
		if (i >= n) add_basis(val, rps[i - n].second);
		md[id] = val;
	}
	sparse_table<Monoid> st(md);
	int Q;
	cin >> Q;
	while (Q--) {
		int x, y, k;
		cin >> x >> y >> k; --x, --y, --k;
		basis bb = Monoid::id();
		hl.path_query(x, y, [&](int u, int v) {
			merge_basis(bb, st.find(u, v));
		});
		printf("%d\n", calc(bb, cost[x] ^ cost[y], k));
	}
	return 0;
}
accuracy: 0.16071428571428573
time_ms: 1440
memory_kb: 356532
final_rank: 16

code_snippet: #include <bits/stdc++.h>
using namespace std;

using Int = unsigned int;
using ll = long long;
struct Edge {
    int id;
    int to;
    Int c;
};
using Graph = vector<vector<Edge>>;
const int LOG_N = 18;
const int WS = 30;
using Bases = vector<Int>;
Bases generateBases(Int x) { return x ? Bases(1, x) : Bases(0); }
int tpb(Int b) { return (b == 0 ? -1 : 31 - __builtin_clz(b)); }
Bases mergeBases(const Bases& lhs, const Bases& rhs) {
    if (lhs.size() == 0) return rhs;
    if (rhs.size() == 0) return lhs;
    if (lhs.size() == WS) return lhs;
    if (rhs.size() == WS) return rhs;

    Bases res = lhs;
    res.insert(res.end(), rhs.begin(), rhs.end());
    int row = 0;
    for (int k = 0; k < res.size(); k++) {
        bool isAllZero = true;
        for (int i = k; i < res.size(); i++) {
            isAllZero = isAllZero && res[i] == 0;
        }
        if (isAllZero) break;
        int pos = k;
        for (int i = k + 1; i < res.size(); i++) {
            pos = (tpb(res[i]) > tpb(res[pos]) ? i : pos);
        }
        swap(res[k], res[pos]);
        int tb = tpb(res[k]);
        for (int i = k + 1; i < res.size(); i++) {
            if ((1 << tb) & res[i]) res[i] ^= res[k];
        }
    }
    while (res.back() == 0) res.pop_back();
    return res;
}

struct Data {
    int N;
    vector<Int> xorAcc;
    vector<int> rep;

    vector<int> dep;
    vector<int> parX;
    vector<int> topX;
    vector<Int> cycleXor;
    vector<vector<int>> dPar;
    vector<vector<int>> dParX;
    vector<vector<Int>> dSum;
    vector<vector<Bases>> dBases;
    int lca(int a, int b) {
        if (dep[a] < dep[b]) std::swap(a, b);
        int u = dep[a] - dep[b];
        for (int i = LOG_N; i >= 0; i--) {
            if ((u >> i) & 1) a = dPar[i][a];
        }
        assert(dep[a] == dep[b]);
        if (a == b) return a;
        for (int i = LOG_N; i >= 0; i--) {
            if (dPar[i][a] != dPar[i][b]) {
                a = dPar[i][a];
                b = dPar[i][b];
            }
        }
        assert(a != b);
        a = dPar[0][a], b = dPar[0][b];
        if (a != b)
            return -1;  // if (g is a forest) and (a and b is disconnected).
        return a;
    }
    Int cycleMove(int x, int y) { return xorAcc[x] ^ xorAcc[y]; }
    void build(int N, int M, Graph& g);

   private:
    int loopCnt = 1;
    vector<vector<Int>> loopsXor;
    vector<vector<int>> loops;
    vector<int> visited;
    vector<int> looped;
    vector<int> used;
    vector<int> decs;
    Graph bg;
    int dfs(int v, Graph& g) {
        // cerr<<"\t\t$DFS"<<" "<<v<<endl;
        if (visited[v]) {
            loops.push_back(vector<int>());
            loopsXor.push_back(vector<Int>());
            decs[v] -= loopCnt;
            return loopCnt++;
        }
        visited[v] = true;
        int sum = 0;
        for (auto& e : g[v]) {
            if (used[e.id]) continue;
            used[e.id] = true;
            if (!visited[e.to]) {
                xorAcc[e.to] = xorAcc[v] ^ e.c;
            }
            int ret = dfs(e.to, g);
            if (ret) {
                looped[v] = true;
                loops[ret - 1].push_back(v);
                Int piyo =
                    loopsXor[ret - 1].empty() ? 0 : loopsXor[ret - 1].back();
                loopsXor[ret - 1].push_back(e.c);
            } else {
                bg[e.to].push_back(Edge{-1, v, e.c});
                bg[v].push_back(Edge{-1, e.to, e.c});
            }
            sum += ret;
        }
        sum += decs[v];
        return sum;
    }
};

void Data::build(int N, int M, Graph& g) {
    xorAcc.resize(N);
    visited.resize(N, 0);
    decs.resize(N, 0);
    bg.resize(N);
    used.resize(M, 0);
    looped.resize(N);
    // cerr << "\t$DFS START" << endl;
    dfs(0, g);
    // cerr << "\t$DFS END" << endl;
    // cerr << "\t\t$LOOP_CNT " << loops.size()<<endl;
    for (int i = 0; i < N; i++) {
        if (!looped[i]) loops.push_back({i}), loopsXor.push_back({0});
    }
    // cerr << "\t\t$V_SIZE " << loops.size()<<endl;

    vector<vector<int>> xToV(N);
    for (int i = 0; i < loops.size(); i++) {
        for (int x : loops[i]) {
            xToV[x].push_back(i);
        }
    }

    rep.resize(N, -1);  // vector<int> rep;

    dep.resize(loops.size());
    parX.resize(loops.size());  // vector<int> parX; // P
    topX.resize(loops.size());  // vector<int> topX; // P
    vector<Int> sumJ(loops.size());
    cycleXor.resize(loops.size());  // vector<int> cycleXor; // V

    dPar.resize(LOG_N + 1,
                vector<int>(loops.size()));  // vector<vector<int>> dPar; // A
    vector<int> xused(N);

    vector<int> vused(loops.size());
    pair<int, int> p = {0, (int)loops[0].back()};
    queue<pair<int, int>> que;
    que.push(p);
    vused[0] = true;
    // cerr << "\t$QUEING START" << endl;
    set<int> s;
    while (!que.empty()) {
        p = que.front();
        que.pop();
        int v = p.first, x = p.second;

        vector<int> xs;
        vector<int> xorxs;
        vector<int> _parX;
        
        for (int i = 0; i < loops[v].size(); i++) {
            cycleXor[v] ^= loopsXor[v][i];
            int x = loops[v][i];
            if (rep[x] == -1) rep[x] = v;
            if (xused[x]) continue;
            xused[x] = true;
            xs.push_back(x);
            xorxs.push_back(0);
            _parX.push_back(x);
            for (auto& e : bg[x]) {
                xs.push_back(e.to);
                xorxs.push_back(e.c);
                _parX.push_back(x);
            }
        }
        
        
        for (int i = 0; i < xs.size(); i++) {
            int y = xs[i];
            if (s.count(y)) continue;
            s.insert(y);
            for (auto vTo : xToV[y]) {
                if (vused[vTo]) continue;
                que.push({vTo, y});
                dPar[0][vTo] = v;
                vused[vTo] = true;
                dep[vTo] = dep[v] + 1;
                parX[vTo] = _parX[i];
                topX[vTo] = y;
                sumJ[vTo] = xorxs[i];
            }
        }
    }
    // cerr<<"\t$QUEING END"<<endl;

    dParX.resize(LOG_N + 1,
                 vector<int>(loops.size()));  // vector<vector<int>> dParX; // A
    dSum.resize(LOG_N + 1,
                vector<Int>(loops.size()));  // vector<vector<int>> dSum; // A
    dBases.resize(
        LOG_N + 1,
        vector<Bases>(loops.size()));  // vector<vector<Bases>> dBases;
    for (int i = 0; i < loops.size(); i++) {
        dParX[0][i] = parX[i];
        dSum[0][i] = sumJ[i];
    }
    // cerr<<"\t$DOUBLING START"<<endl;
    for (int i = 0; i < LOG_N; i++) {
        for (int j = 0; j < loops.size(); j++) {
            dPar[i + 1][j] = dPar[i][dPar[i][j]];
            dParX[i + 1][j] = dParX[i][dPar[i][j]];
            dSum[i + 1][j] = dSum[i][j] ^ dSum[i][dPar[i][j]];
            dBases[i + 1][j] = mergeBases(dBases[i][j], dBases[i][dPar[i][j]]);
            if (dParX[i][j] != topX[dPar[i][j]]) {
                dSum[i + 1][j] ^= cycleMove(dParX[i][j], topX[dPar[i][j]]);
                dBases[i + 1][j] = mergeBases(
                    dBases[i + 1][j], generateBases(cycleXor[dPar[i][j]]));
            }
        }
    }
    // cerr << "\t$DOUBLING END" << endl;
}

void answer_query(Data& dat, int Q, vector<int>& u, vector<int>& v,
                  vector<ll>& k) {
    struct T {
        int z;
        Int sum;
        Bases bases;
    };
    auto merge = [&](const T& lhs, const T& rhs) {
        return T{rhs.z, lhs.sum ^ rhs.sum, mergeBases(lhs.bases, rhs.bases)};
    };
    auto cycleMove = [&](T& t, int to) {
        if (t.z != to) {
            T tmp = T{to, dat.cycleMove(t.z, to),
                      generateBases(dat.cycleXor[dat.rep[t.z]])};
            t = merge(t, tmp);
        }
    };
    auto climb = [&](int x, int cnt) {
        T res = {x, 0, Bases(0)};
        int id = 0;
        while (cnt) {
            if (cnt & 1) {
                int v = dat.rep[res.z];
                cycleMove(res, dat.topX[v]);
                T tmp = {dat.dParX[id][v], dat.dSum[id][v], dat.dBases[id][v]};
                res = merge(res, tmp);
            }
            cnt >>= 1;
            id++;
        }
        return res;
    };
    for (int i = 0; i < Q; i++) {
        int x = u[i], y = v[i];
        int a = dat.rep[x], b = dat.rep[y];
        int c = dat.lca(a, b);
        int ca = dat.dep[a] - dat.dep[c];
        int cb = dat.dep[b] - dat.dep[c];
        T ta = climb(x, ca);
        T tb = climb(y, cb);
        cycleMove(ta, tb.z);
        T t = merge(ta, tb);

        ll num = k[i];
        ll patternCnt = 1LL << t.bases.size();
        if (patternCnt <= num) {
            cout << -1 << "\n";
            continue;
        }
        Int ans = t.sum;
        for (int i = 0; i < t.bases.size(); i++) {
            int tb = tpb(t.bases[i]);
            bool bnum = (num >> (t.bases.size() - 1 - i)) & 1;
            bool bans = (ans >> tb) & 1;
            if (bans ^ bnum) {
                ans ^= t.bases[i];
            }
        }
        cout << ans << "\n";
    }
}
void solve(int N, int M, Graph& g, int Q, vector<int>& u, vector<int>& v,
           vector<ll>& k) {
    Data dat;
    // cerr << "#Build Start" << endl;
    dat.build(N, M, g);
    // cerr << "#Build Finished" << endl;
    answer_query(dat, Q, u, v, k);
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int N, M;
    cin >> N >> M;
    Graph g(N);
    for (int i = 0; i < M; i++) {
        int u, v;
        Int c;
        cin >> u >> v >> c;
        u--, v--;
        g[u].push_back(Edge{i, v, c});
        g[v].push_back(Edge{i, u, c});
    }
    int Q;
    cin >> Q;
    vector<int> u(Q), v(Q);
    vector<ll> k(Q);
    for (int i = 0; i < Q; i++)
        cin >> u[i] >> v[i] >> k[i], u[i]--, v[i]--, k[i]--;
    // cerr << "#INPUT FINISHED" << endl;
    solve(N, M, g, Q, u, v, k);
    return 0;
}
accuracy: 1
time_ms: 4840
memory_kb: 187328
final_rank: 11

code_snippet: #include <bits/stdc++.h>
using namespace std;
using pii = pair<int, int>;
using graph = vector<vector<pii>>;

const int WS = 30;

using basis = array<int, WS>;

const int BN = 18; // larger than log2_(M + 1)

template <typename T>
constexpr T INF = INT_MAX;

template <>
constexpr pii INF<pii> = pii(INT_MAX, INT_MAX);

void add_basis(basis& bs, int x) {
	for (int b = WS - 1; b >= 0 && x != 0; b--) {
		if (x & (1 << b)) {
			if (bs[b]) {
				x ^= bs[b];
			}
			else {
				bs[b] = x;
				break;
			}
		}
	}
}

void merge_basis(basis& bs, const basis& ad) {
	for (int bb = WS - 1; bb >= 0; bb--) if (ad[bb] != 0) {
		int x = ad[bb];
		for (int b = bb; b >= 0 && x > 0; b--) {
			if (x & (1 << b)) {
				if (bs[b]) {
					x ^= bs[b];
				}
				else {
					bs[b] = x;
					break;
				}
			}
		}
	}
}

void dfs(int v, int prev, const graph& G, vector<pair<vector<int>, int>>& rps, vector<int>& a, vector<int>& cost, vector<int>& tmp) {
	tmp.push_back(v);
	a[v] = prev == -1 ? 0 : a[prev] + 1;
	for (const auto& e : G[v]) if (e.first != prev) {
		if (a[e.first] == -1) {
			cost[e.first] = cost[v] ^ e.second;
			dfs(e.first, v, G, rps, a, cost, tmp);
		}
		else if (a[e.first] < (int)tmp.size() && tmp[a[e.first]] == e.first) {
			rps.emplace_back(vector<int>(tmp.begin() + a[e.first], tmp.end()), cost[v] ^ cost[e.first] ^ e.second);
		}
	}
	tmp.pop_back();
}

vector<pair<vector<int>, int>> enum_roop(const graph& G, vector<int>& cost) {
	const int n = G.size();
	vector<int> a(n, -1), tmp;
	vector<pair<vector<int>, int>> rps;
	dfs(0, -1, G, rps, a, cost, tmp);
	return rps;
}

vector<vector<int>> make_tree(const graph &G, const vector<pair<vector<int>, int>>& rps) {
	const int n = G.size(), s = rps.size();
	vector<set<int>> gs(n + s);
	for (int i = 0; i < n; i++) {
		for (const auto& e : G[i]) {
			gs[i].insert(e.first);
		}
	}
	for (int i = 0; i < s; i++) {
		int v = n + i, x = rps[i].first.size();
		for (int j = 0; j < x; j++) {
			gs[rps[i].first[j]].erase(rps[i].first[(j + 1) % x]);
			gs[rps[i].first[j]].erase(rps[i].first[(j + x - 1) % x]);
			gs[rps[i].first[j]].insert(v);
			gs[v].insert(rps[i].first[j]);
		}
	}
	vector<vector<int>> g;
	for (int i = 0; i < (int)gs.size(); i++) {
		g.emplace_back(gs[i].begin(), gs[i].end());
	}
	return g;
}

struct Monoid {
	using type = basis;
	static type id() {
		basis b;
		for (int i = 0; i < WS; i++) b[i] = 0;
		return b;
	}
	static type op(const type& l, const type & r) {
		auto tmp = l;
		merge_basis(tmp, r);
		return tmp;
	}
};

template <typename M>
class sparse_table {
	using T = typename M::type;
	const int n;
	vector<vector<T>> t;
public:
	sparse_table(const vector<T>& b) : n(b.size()), t(1, b) {
		for (int i = 2, j = 1, p = 0; i <= n; i <<= 1, j++, p = 0) {
			t.emplace_back(n);
			for (int k = 0; k + i <= n; k++)
				t[j][p++] = M::op(t[j - 1][k], t[j - 1][k + (i >> 1)]);
		}
	}
	T find(int l, int r) const { // [l, r)
		assert(0 <= l && l < r && r <= n);
		int i = 31 - __builtin_clz(r - l);
		return M::op(t[i][l], t[i][r - (1 << i)]);
	}
};

class heavy_light_decomposition {
	const int n;
	vector<vector<int>> g;
	vector<int> par, head, in, out;
	void dfs1(int rt) {
		vector<int> size(n, 1);
		vector<size_t> iter(n);
		vector<pair<int, int>> stp;
		stp.reserve(n);
		stp.emplace_back(rt, -1);
		while (!stp.empty()) {
			int v = stp.back().first;
			if (iter[v] < g[v].size()) {
				if (g[v][iter[v]] != stp.back().second) {
					stp.emplace_back(g[v][iter[v]], v);
				}
				++iter[v];
				continue;
			}
			par[v] = stp.back().second;
			for (auto& u : g[v]) if (u == par[v]) {
				if (u != g[v].back()) swap(u, g[v].back());
				g[v].pop_back();
				break;
			}
			for (auto& u : g[v]) {
				size[v] += size[u];
				if (size[u] > size[g[v].front()]) swap(u, g[v].front());
			}
			stp.pop_back();
		}
	}
	void dfs2(int rt) {
		int it = 0;
		vector<size_t> iter(n);
		vector<int> st; st.reserve(n);
		st.push_back(rt);
		while (!st.empty()) {
			int v = st.back();
			if (!iter[v]) in[v] = it++;
			if (iter[v] < g[v].size()) {
				int u = g[v][iter[v]];
				head[u] = iter[v] ? u : head[v];
				st.push_back(u);
				++iter[v];
				continue;
			}
			out[v] = it;
			st.pop_back();
		}
	}
public:
	heavy_light_decomposition(int n_)
		: n(n_), g(n), par(n), head(n), in(n), out(n) {}
	heavy_light_decomposition(const vector<vector<int>>& G)
		: n(G.size()), g(G), par(n), head(n), in(n), out(n) {}
	void add_edge(int u, int v) {
		g[u].push_back(v);
		g[v].push_back(u);
	}
	void build(int rt = 0) {
		dfs1(rt);
		head[rt] = rt;
		dfs2(rt);
	}
	int get_id(int v) {
		return in[v];
	}
	int get_lca(int u, int v) {
		while (true) {
			if (in[u] > in[v]) swap(u, v);
			if (head[u] == head[v]) return u;
			v = par[head[v]];
		}
	}
	void path_query(int u, int v, function<void(int, int)> f) {
		while (true) {
			if (in[u] > in[v]) swap(u, v);
			f(max(in[head[v]], in[u]), in[v] + 1);
			if (head[u] == head[v]) return;
			v = par[head[v]];
		}
	}
	void path_query(int u, int v, function<void(int, int, bool)> f) {
		while (true) {
			if (in[u] <= in[v]) {
				f(max(in[head[v]], in[u]), in[v] + 1, false);
				if (head[u] == head[v]) return;
				v = par[head[v]];
			}
			else {
				f(max(in[head[u]], in[v]), in[u] + 1, true);
				if (head[u] == head[v]) return;
				u = par[head[u]];
			}
		}
	}
	void edge_path_query(int u, int v, function<void(int, int)> f) {
		while (true) {
			if (in[u] > in[v]) swap(u, v);
			if (head[u] != head[v]) {
				f(max(in[head[v]], in[u]), in[v] + 1);
				v = par[head[v]];
			}
			else {
				if (u != v) f(in[u] + 1, in[v] + 1);
				break;
			}
		}
	}
	void edge_path_query(int u, int v, function<void(int, int, bool)> f) {
		while (true) {
			if (in[u] > in[v]) {
				if (head[u] != head[v]) {
					f(max(in[head[u]], in[v]), in[u] + 1, true);
					u = par[head[u]];
				}
				else {
					if (u != v) f(in[v] + 1, in[u] + 1, false);
					break;
				}
			}
			else {
				if (head[u] != head[v]) {
					f(max(in[head[v]], in[u]), in[v] + 1, false);
					v = par[head[v]];
				}
				else {
					if (u != v) f(in[u] + 1, in[v] + 1, false);
					break;
				}
			}
		}
	}
	void subtree_query(int v, function<void(int, int)> f) {
		f(in[v], out[v]);
	}
};

int calc(const basis& bs, int x, int k) {
	int cnt = 0;
	for (int i = 0; i < WS; i++) cnt += bs[i] != 0;
	if (k >= (1 << cnt)) return -1;
	for (int i = WS - 1; i >= 0; i--) if (bs[i] != 0) {
		--cnt;
		if (k < (1 << cnt)) {
			x = min(x, x ^ bs[i]);
		}
		else {
			k -= 1 << cnt;
			x = max(x, x ^ bs[i]);
		}
	}
	return x;
}

int main()
{
	ios::sync_with_stdio(false), cin.tie(0);
	int N, M;
	cin >> N >> M;
	graph G(N);
	for (int i = 0; i < M; i++) {
		int u, v, c;
		cin >> u >> v >> c; --u, --v;
		G[u].emplace_back(v, c);
		G[v].emplace_back(u, c);
	}
	vector<int> cost(N);
	auto rps = enum_roop(G, cost);
	auto g = make_tree(G, rps);
	const int n = N;
	N = g.size();
	assert(N == M + 1);
	heavy_light_decomposition hl(g);
	hl.build();
	vector<Monoid::type> md(N, Monoid::id());
	for (int i = 0; i < N; i++) {
		auto val = Monoid::id();
		int id = hl.get_id(i);
		if (i >= n) add_basis(val, rps[i - n].second);
		md[id] = val;
	}
	sparse_table<Monoid> st(md);
	int Q;
	cin >> Q;
	while (Q--) {
		int x, y, k;
		cin >> x >> y >> k; --x, --y, --k;
		basis bb = Monoid::id();
		hl.path_query(x, y, [&](int u, int v) {
			merge_basis(bb, st.find(u, v));
		});
		printf("%d\n", calc(bb, cost[x] ^ cost[y], k));
	}
	return 0;
}
accuracy: 1
time_ms: 5650
memory_kb: 380964
final_rank: 13