发现题面很类似这样一个经典问题:
给出一堆数字,再给你一个数,找到那堆数中与这个数异或最大的
这个问题很经典了,直接建立字典树
对于每个数,二进制分解,然后把二进制从高位到低位插入到字典树上
形成一个深度为 31 31 31的字典树,异或最大值就不停的贪心选择走 0 0 0还是走 1 1 1即可
但是树上路径没那么简单了,不再是简单的两数异或…
但是如果处理一个 d i s [ i ] dis[i] dis[i]数组,表示根到自己的异或值
那么 i i i和 j j j的路径异或值不就是 d i s [ i ] ⊕ d i s [ j ] dis[i]\oplus dis[j] dis[i]⊕dis[j]吗
因为他们的 d i s [ l c a ] dis[lca] dis[lca]部分异或了两次,消掉了
这样一来,转化为把 d i s [ i ] dis[i] dis[i]插入字典树了
#include <bits/stdc++.h>
using namespace std;
const int maxn=8e6+10;
int n;
struct edge{
int to,nxt,w;
}d[maxn]; int head[maxn],cnt=1;
void add(int u,int v,int w){
d[++cnt]=(edge){v,head[u],w},head[u]=cnt;
}
int dis[maxn];
void dfs(int u,int fa)
{
for(int i=head[u];i;i=d[i].nxt )
{
int v=d[i].to;
if( v==fa ) continue;
dis[v]=dis[u]^d[i].w;
dfs(v,u);
}
}
int zi[maxn][3],tot;
void insert(int x)
{
int now=0;
for(int i=30;i>=0;i--)
{
int k=0;
if( x&(1<<i) ) k=1;
if( !zi[now][k] ) zi[now][k]=++tot;
now=zi[now][k];
}
}
int ask(int x)
{
int ans=0,now=0;
for(int i=30;i>=0;i--)
{
int k=1;
if( x&(1<<i) ) k=0;
if( zi[now][k] ) ans+=(1<<i),now=zi[now][k];
else now=zi[now][k^1];
}
return ans;
}
int main()
{
cin >> n;
for(int i=1;i<n;i++)
{
int l,r,w; cin >> l >> r >> w;
add(l,r,w); add(r,l,w);
}
dfs(1,0);
int ans=0;
for(int i=1;i<=n;i++) insert(dis[i]);
for(int i=1;i<=n;i++) ans=max(ans,ask(dis[i]) );
cout << ans;
}