Explanation
首先应该想到用Trie解决两个数xor最大值的模板(可见CH1602)。具体来说,建一棵字典树,这棵字典树每个节点仅有两个分支0和1,然后把每一个数字的二进制形式从高位到低位插入Trie。若要查询最大值,就在Trie上沿尽量当前位的反方向行走。若能够这样走,则ans该位为1,否则为0。
在这道题中,我们先用一遍dfs处理出每个节点到根节点路径上所有边权的xor和(设为sum[])。每两个节点(假设为x和y)之间的xor和即为sum[x] xor sum[y],因为重复路径上的权值自己xor自己,结果为0(a xor a = 0),对结果没有影响。
注意:1.本题中默认根节点为0;2.有多组数据。
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAX_N = 100000 + 10;
int Head[MAX_N], Next[MAX_N << 1], V[MAX_N << 1], W[MAX_N << 1], tot;
int T[MAX_N * 30][2], cnt;
int sum[MAX_N], n, ans;
void Add(int u, int v, int w) {
V[++tot] = v; W[tot] = w;
Next[tot] = Head[u]; Head[u] = tot;
}
void dfs(int u, int fa) {
for (int i = Head[u]; i; i = Next[i]) {
int v = V[i], w = W[i];
if (v == fa) continue;
sum[v] = sum[u] ^ w;
dfs(v, u);
}
}
void Insert(int str) {
int cur = 0;
for (int i = 31; i >= 0; i--) {
int ch = (str >> i) & 1;
if (!T[cur][ch]) T[cur][ch] = ++cnt;
cur = T[cur][ch];
}
}
int Query(int str) {
int cur = 0, ret = 0;
for (int i = 31; i >= 0; i--) {
int ch = (str >> i) & 1;
ret <<= 1;
if (T[cur][ch ^ 1]) ret |= 1, cur = T[cur][ch ^ 1];
else cur = T[cur][ch];
}
return ret;
}
int main() {
int u, v, w;
while (scanf("%d", &n) != EOF && n) {
memset(Head, 0, sizeof Head); tot = 0;
memset(T, 0, sizeof T); cnt = 0;
memset(sum, 0, sizeof sum); ans = 0;
for (int i = 1; i < n; i++) {
scanf("%d%d%d", &u, &v, &w);
Add(u, v, w); Add(v, u, w);
}
dfs(0, -1);
for (int i = 1; i <= n; i++) {
ans = max(ans, Query(sum[i]));
Insert(sum[i]);
}
printf("%d\n", ans);
}
return 0;
}
Reference
《算法竞赛进阶指南》p.76
博客介绍用Trie解决两个数xor最大值的方法,先建字典树,将数字二进制形式从高位到低位插入,查询时沿当前位反方向行走。还提到在具体题目中,用dfs处理节点到根节点边权xor和,两节点间xor和为对应sum值的xor。
309

被折叠的 条评论
为什么被折叠?



