题目大意
给定一棵边权树, 要求一条两个点之间的最短路径, 使得路径上所有边的权值的异或最大.
题解
考虑异或的性质:
- 交换律\(a \oplus b = b \oplus a\)
- 结合律\((a \oplus b) \oplus c = a \oplus (b \oplus c)\)
- 消去率\(a \oplus b = a \oplus c \oplus b \oplus c\)
所以我们发现, 假设\(anc\)是\(u\)和\(v\)的祖先, \(u\)和\(v\)分别是路径的两端, 则\(u \to anc \to v = (root \to anc \to u) \oplus (root \to anc \to v)\)
DFS一次, 到达每个点的时候都在trie树上找最大异或值即可.
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
const int N = (int)1e5;
namespace Zeonfai
{
inline int getInt()
{
int a = 0, sgn = 1;
char c;
while(! isdigit(c = getchar()))
if(c == '-')
sgn *= -1;
while(isdigit(c))
a = a *10 + c - '0', c = getchar();
return a * sgn;
}
}
struct trieTree
{
struct node
{
node *suc[2];
inline node()
{
suc[0] = suc[1] = NULL;
}
};
node *rt, *lst;
inline void initialize()
{
rt = new node;
}
inline void _insert(int k)
{
node *u = lst->suc[k];
if(lst->suc[k] == NULL)
lst->suc[k] = new node;
lst = lst->suc[k];
}
inline void insert(int w)
{
lst = rt;
for(int i = 30; ~ i; -- i)
_insert(w >> i & 1);
}
inline int query(int w)
{
int res = 0;
node *u = rt;
for(int i = 30; ~ i; -- i)
{
int k = w >> i & 1;
if(u->suc[k ^ 1] != NULL)
u = u->suc[k ^ 1], res += 1 << i;
else
u = u->suc[k];
}
return res;
}
}trie;
int ans = 0;
struct tree
{
struct edge
{
int v, w, nxt;
}edg[N << 1];
int tp;
int hd[N + 1];
inline void initialize()
{
tp = 0;
memset(hd, -1, sizeof(hd));
}
inline void addEdge(int u, int v, int w)
{
edg[tp].v = v, edg[tp].w = w, edg[tp].nxt = hd[u];
hd[u] = tp ++;
}
inline void DFS(int u, int pre, int w)
{
trie.insert(w);
ans = std::max(ans, trie.query(w));
for(int p = hd[u]; ~ p; p = edg[p].nxt)
if(edg[p].v ^ pre)
DFS(edg[p].v, u, w ^ edg[p].w);
}
}org;
int main()
{
#ifndef ONLINE_JUDGE
freopen("XSY2340.in", "r", stdin);
#endif
using namespace Zeonfai;
int n = getInt();
org.initialize();
for(int i = 1; i < n; ++ i)
{
int u = getInt(), v = getInt(), w = getInt();
org.addEdge(u, v, w), org.addEdge(v, u, w);
}
trie.initialize();
org.DFS(1, -1, 0);
printf("%d\n", ans);
}