题意:给定一棵n个点的带权树,结点下标从1开始到N。寻找树中找两个结点,求最长的异或路径。异或路径指的是指两个结点之间唯一路径上的所有边权的异或。
题解:01trie+维护异或最值
指定rtrtrt,用Xor(u,v)Xor(u,v)Xor(u,v)表示uuu与vvv两点之间的边权异或,那么Xor(u,v)=Xor(u,rt)⨁Xor(v,rt)Xor(u,v) = Xor(u,rt)\bigoplus Xor(v,rt)Xor(u,v)=Xor(u,rt)⨁Xor(v,rt),其实这只是计算了lca路径上的边权异或,因为lca以上的边两两相同,异或为0。
对于每个点uuu,将Xor(rt,u)Xor(rt,u)Xor(rt,u)插入trie中,这样就可以很快找到另一个点vvv,使得Xor(u,v)Xor(u,v)Xor(u,v)最大。求Xor(rt,u)Xor(rt,u)Xor(rt,u)我们dfs即可。
如何去找呢?我们贪心,即从Xor(rt,u)Xor(rt,u)Xor(rt,u)的最高位开始,若该位为1,我们找有没有0;该位为0,我们找有没有1,那么我们就可以保证当前异或值始终最大了。
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<fstream>
#include<set>
#include<map>
#include<sstream>
#include<iomanip>
#define ll long long
using namespace std;
const int maxn = 1e5 + 5;
const int maxm = 2e5 + 5;
struct node {
int v, next, w;
}edge[maxm];
int head[maxn], k;
void add(int u, int v, int w) {
edge[++k].next = head[u];
edge[k].v = v;
edge[k].w = w;
head[u] = k;
}
int d[maxn], ans;
struct trie {
int nex[maxn * 30][2], cnt;
void insert(int x) {
int p = 0;
for (int i = 30; i >= 0; i--) { //数据范围2^30
int c = (x >> i) & 1;
if (!nex[p][c]) nex[p][c] = ++cnt;
p = nex[p][c];
}
}
void find(int x) {
int p = 0, res = 0;
for (int i = 30; i >= 0; i--) {
int c = (x >> i) & 1;
if (nex[p][c ^ 1]) { //贪心 最高位不同的子树走
p = nex[p][c ^ 1];
res |= 1 << i;
}
else p = nex[p][c];
}
ans = max(ans, res);
}
}trie;
void dfs(int u, int pre) {
trie.insert(d[u]);
trie.find(d[u]);
for (int i = head[u]; i; i = edge[i].next) {
int v = edge[i].v;
int w = edge[i].w;
if (v == pre) continue;
d[v] = d[u] ^ w; //边权异或
dfs(v, u);
}
}
int n, u, v, w;
int main() {
scanf("%d", &n);
for (int i = 1; i < n; i++) {
scanf("%d%d%d", &u, &v, &w);
add(u, v, w);
add(v, u, w);
}
dfs(1, 0);
printf("%d\n", ans);
return 0;
}