题目大意
给你一颗树。求树上最长的异或路径。
异或路径指的是指两个结点之间唯一路径上的所有边权的异或。
解
可以发现,若一个点异或两次,它的贡献值为0,约等于没有经过这个点。
于是我们可以预处理出根节点到其它点的异或 xorr 。
点 x 到点 y 间的异或路径就为 xorr[x] xor xorr[y] 。
那么问题就变成了: 再xorr数组里求两个数异或和最大。
就和这题一样了。
从高位往低位处理,便于使异或结果最大。
构建Trie树。
从高位往地位处理,每次优先选与当前位不同的路径(这样和最大),若没有,就选择当前位相同的走下去
代码
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <cmath>
using namespace std;
char ch;
int n, tot, u, v, lans, ans, w, z, t, l[1000001], b[1000001], xorr[1000001], f[1000001][3];
struct asdf {
int to, z, next;
} a[10000001];
void dfs(int d) {
for (int i = l[d]; i; i = a[i].next)
if (b[a[i].to] == 0) {
b[a[i].to] = 1; //标记
xorr[a[i].to] = xorr[d] xor a[i].z; //得出值
int ww = 0;
for (int j = 30; j >= 0; --j) { //将这个值放入Trie树
int k = (xorr[a[i].to] >> j) % 2;
if (f[ww][k] == 0)
f[ww][k] = ++tot;
ww = f[ww][k];
}
dfs(a[i].to);
}
}
int main() {
scanf("%d", &n);
for (int i = 1; i < n; ++i) {
scanf("%d%d%d", &u, &v, &z);
a[++t] = (asdf){ v, z, l[u] };
l[u] = t;
a[++t] = (asdf){ u, z, l[v] };
l[v] = t;
}
dfs(1); //以1号点为根节点
for (int i = 2; i <= n; ++i) { //枚举某个数
w = lans = 0;
for (int j = 30; j >= 0; --j) { //按上面的方法使异或的值最大
int k = (xorr[i] >> j) % 2;
if (f[w][(k + 1) % 2] != 0) {
w = f[w][(k + 1) % 2];
lans += 1 << j;
} else if (f[w][k] != 0)
w = f[w][k];
else
break;
}
ans = max(ans, lans); //取最大答案
}
printf("%d", ans);
}