G. Yasya and the Mysterious Tree
Yasya was walking in the forest and accidentally found a tree with $n$ vertices. A tree is a connected undirected graph with no cycles.
Next to the tree, the girl found an ancient manuscript with $m$ queries written on it. The queries can be of two types.
The first type of query is described by the integer $y$. The weight of each edge in the tree is replaced by the bitwise exclusive OR of the weight of that edge and the integer $y$.
The second type is described by the vertex $v$ and the integer $x$. Yasya chooses a vertex $u$ ($1 \le u \le n$, $u \neq v$) and mentally draws a bidirectional edge of weight $x$ from $v$ to $u$ in the tree.
Then Yasya finds a simple cycle in the resulting graph and calculates the bitwise exclusive OR of all the edges in it. She wants to choose a vertex $u$ such that the calculated value is maximum. This calculated value will be the answer to the query. It can be shown that such a cycle exists and is unique under the given constraints (independent of the choice of $u$). If an edge between $v$ and $u$ already existed, a simple cycle is the path $v \to u \to v$.
Note that the second type of query is performed mentally, meaning the tree does not change in any way after it.
Help Yasya answer all the queries.
Input
The first line contains an integer $t$ ($1 \le t \le 10^4$) — the number of test cases.
The descriptions of the test cases follow.
The first line of each test case contains two integers $n$, $m$ ($2 \le n \le 2 \cdot 10^5$, $1 \le m \le 2 \cdot 10^5$) — the number of vertices in the tree and the number of queries.
The next $n - 1$ lines of each test case contain three integers $v$, $u$, $w$ ($1 \le v, u \le n$, $1 \le w \le 10^9$) — the ends of some edge in the tree and its weight.
It is guaranteed that the given set of edges forms a tree.
The next $m$ lines of each test case describe the queries:
- ^ $y$ ($1 \le y \le 10^9$) — parameter of the first type query;
- ? $v$ $x$ ($1 \le v \le n$, $1 \le x \le 10^9$) — parameters of the second type query.
It is guaranteed that the sum of $n$ over all test cases does not exceed $2 \cdot 10^5$. The same is guaranteed for $m$.
Output
For each test case, output the answers to the queries of the second type.
Examples
input
2
3 7
1 2 1
3 1 8
^ 5
? 2 9
^ 1
? 1 10
^ 6
? 3 1
? 2 9
5 6
1 2 777
3 2 2812
4 1 16
5 3 1000000000
^ 4
? 3 123
? 5 1000000000
^ 1000000000
? 1 908070
? 2 1
output
13 15 11 10
1000000127 2812 999756331 999999756
input
3
8 4
8 6 3
6 3 4
2 5 4
7 6 2
7 1 10
4 1 4
5 1 2
^ 4
^ 7
? 7 8
? 4 10
5 6
3 1 4
2 3 9
4 3 6
5 2 10
? 5 7
^ 1
^ 8
? 4 10
? 1 9
? 3 6
4 2
2 1 4
4 3 5
2 3 4
^ 13
? 1 10
output
14 13
13 8 11 11
10
解题思路
对于询问操作,本质上就是找到一个点 $u \, (u \ne v)$ 使得 $u$、$v$ 两点构成路径的边权异或和,与 $x$ 异或后的结果最大。为了求得任意一条路径的异或和,先通过 dfs 预处理出每个点 $u$ 到根节点(固定节点 $1$ 为根)路径上的异或和 $s_u$。那么任意两点 $u$、$v$ 构成路径的异或和就是 $s_u \oplus s_v$。假设 $p = \mathrm{lca}(u,v)$,路径 $u \to p \to v$ 的异或和就是 $(s_u \oplus s_p) \oplus (s_v \oplus s_p) = s_u \oplus s_v$。
由于 $s_v$ 和 $x$ 是固定的,所以就是找到一个 $s_u$,使得 $s_u \oplus (s_v \oplus x)$ 的结果最大。这就是一个经典的问题,将每个 $s_u$ 的二进制位从高到低存储到 Trie 中,然后从高位到低位贪心地找到使得异或 $s_v \oplus x$ 结果最大的 $s_u$。又因为 $u \ne v$,因此在查询前需要在 Trie 中删掉 $s_v$,只需逻辑删除即可,即给 Trie 的每个节点开个计数器,在遍历 $s_v$ 的节点时只需对其计数器减 $1$ 即可。查询完后还要再把 $s_v$ 插入到 Trie 中。
再考虑修改操作对 $s_u$ 的影响。如果 $1 \to u$ 的路径上有偶数条边,显然 $s_u$ 的结果不会改变。否则 $1 \to u$ 的路径上有奇数条边,那么有 $s_u \gets s_u \oplus x$。对于每次修改操作,显然可以先把路径长度为奇数的 $s_u$ 从 Trie 中删除,再插入 $s_u \oplus x$,也显然会超时。干脆根据路径长度的奇偶性维护两个 Trie,当查询偶数的 Trie 时,找的是异或 $s_v \oplus x$ 的最大结果;查询奇数的 Trie 时,找的是异或 $s_v \oplus x \oplus \mathrm{sum}$ 的最大结果,其中 $\mathrm{sum}$ 是前面所有修改操作的异或和。
AC 代码如下,时间复杂度为 $O((n+m) \log{A})$:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 5, M = N * 2, K = N * 30;
int h[N], e[M], wt[M], ne[M], idx;
int s[N], d[N];
int tr[2][K][2], cnt[K];
void add(int u, int v, int w) {
e[idx] = v, wt[idx] = w, ne[idx] = h[u], h[u] = idx++;
}
void modify(int tr[][2], int x, int c){
int p = 0;
for (int i = 29; i >= 0; i--) {
int t = x >> i & 1;
if (!tr[p][t]) tr[p][t] = ++idx;
p = tr[p][t];
cnt[p] += c;
}
}
void dfs(int u, int p) {
modify(tr[d[u]], s[u], 1);
for (int i = h[u]; i != -1; i = ne[i]) {
int v = e[i];
if (v == p) continue;
s[v] = s[u] ^ wt[i];
d[v] = d[u] ^ 1;
dfs(v, u);
}
}
int query(int tr[][2], int x) {
int p = 0, ret = 0;
for (int i = 29; i >= 0; i--) {
int t = x >> i & 1;
if (cnt[tr[p][t ^ 1]]) p = tr[p][t ^ 1], ret |= 1 << i;
else p = tr[p][t];
}
return ret;
}
void solve() {
int n, m;
cin >> n >> m;
idx = 0;
memset(h, -1, n + 1 << 2);
for (int i = 0; i < n - 1; i++) {
int u, v, w;
cin >> u >> v >> w;
add(u, v, w), add(v, u, w);
}
idx = 0;
for (int i = 0; i <= 1; i++) {
for (int j = 0; j < n * 30; j++) {
tr[i][j][0] = tr[i][j][1] = 0;
}
}
memset(cnt, 0, n * 30 + 1 << 2);
dfs(1, 0);
int sum = 0;
while (m--) {
char c;
int x, y;
cin >> c >> x;
if (c == '^') {
sum ^= x;
}
else {
cin >> y;
modify(tr[d[x]], s[x], -1); // 先在相应的Trie中删除s[u]
// 分别查询(u,v)路径长度为偶数、奇数的最大值
cout << max(query(tr[d[x]], s[x] ^ y), query(tr[d[x] ^ 1], s[x] ^ y ^ sum)) << ' ';
modify(tr[d[x]], s[x], 1); // 重新插入s[u]
}
}
cout << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
参考资料
Codeforces Round 950 (Div. 3) Editorial:https://codeforces.com/blog/entry/130135
标签:10,le,int,tr,Tree,异或,Mysterious,oplus,Yasya From: https://www.cnblogs.com/onlyblues/p/18233660