P4551题解
最长异或路径
题意:
给定一棵 \(n\) 个点 \(n-1\) 条边的树,求树上任意两点之间唯一路径上的所有边权异或的最大值。
分析:
我们首先来看一个示例:

我们在求两点间异或路径时可以分为两种情况:
1.两点没有祖孙关系(如图中的节点 \(2\) 和节点 \(3\))
2.一点时另一点的祖宗(如图中的节点 \(2\) 和节点 \(4\))
第一种情况,显然可以通过求两点分别到他们最近公共祖先的路径异或值。由于同一个数如果被异或两次就会被抵消,因此我们可以通过求解两点分别到根节点的路径异或值后两值计算得到。
第二种情况,是求解一个点到它祖宗的路径异或值,同样可以转化为两点分别到根节点的路径异或值再计算得到。
因此,我们只需预处理出每个点到根节点的异或路径值,便可计算。
接下来,有了每个点到根节点的异或值,我们便可通过把每个值转化为一个 \(32\) 位二进制数用Trie树存储,再在Trie中做类似检索的操作,每一步都尽量选择与待配对的数的每一位的值相反的节点向下访问,若没有则访问值相同的节点。由于异或运算遵循相同得0,不同得1的准则,因此该方法可以得到最大的异或值。
Code:
/*
user:leoair
time:2022.4.11
*/
#include <bits/stdc++.h>
#define int long long
#define N 100010
using namespace std;
int n, ans, cnt, tot = 1, d[N], head[N], trie[N << 5][2];//trie数组莫忘开大32倍
bool v[N];
struct xcj{
int to, nxt, value;
} e[N << 1];
inline int read(){
int s = 0, w = 1;
char ch = getchar();
for (; ch < '0' || ch > '9'; w *= ch == '-' ? -1 : 1, ch = getchar());
for (; ch >= '0' && ch <= '9'; s = s * 10 + ch - '0', ch = getchar());
return s * w;
}
void add(int u, int v, int w){e[++cnt] = {v, head[u], w}, head[u] = cnt;}
void dfs(int x){
for (int i = head[x]; i; i = e[i].nxt){
int y = e[i].to, z = e[i].value;
if (v[y]) continue;
v[y] = 1, d[y] = d[x] ^ z, dfs(y);
}
}
void insert(int x, int p = 1){
for (int i = 31, k; ~i; --i){
k = (x >> i) & 1;
if (!trie[p][k]) trie[p][k] = ++tot;
p = trie[p][k];
}
}
int search(int x, int p = 1, int num = 0){
for (int i = 31, k; ~i; --i){
k = (x >> i) & 1;
if (trie[p][k ^ 1]) num = (num << 1) + k ^ 1, p = trie[p][k ^ 1];
else num = (num << 1) + k, p = trie[p][k];
}
return x ^ num;
}
signed main(){
n = read();
for (int i = 1, u, v, w; i < n; ++i) u = read(), v = read(), w = read(), add(u, v, w), add(v, u, w);
v[1] = 1, dfs(1);
for (int i = 1; i <= n; ++i){
insert(d[i]);
if (i > 1) ans = max(ans, search(d[i]));
}
printf("%lld\n", ans);
return 0;
}
浙公网安备 33010602011771号