The Xor

发现题面很类似这样一个经典问题:

给出一堆数字,再给你一个数,找到那堆数中与这个数异或最大的

这个问题很经典了,直接建立字典树

对于每个数,二进制分解,然后把二进制从高位到低位插入到字典树上

形成一个深度为 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;
}