题目大意:给定棵树,两节点间有权值,求一条路径,边上的权值异或值最大,并输出这个最大的异或值。
因为 (a^c)^(c^b)=a^b ; 相同的部分c抵消掉,在树中,如果求出两点到根的异或之和A,B ,则两点间的异或之和就等于A^B ,A和B里都有相同的部分c ;
要求的是最大的这样的路径是多少。讲到树上的两点的xor,一个常用的手段就是随便选一个根,然后深搜记下每个点v到根的xor路径 w[v],那么两点x,y路径的xor就是w[x]^w[y].
深搜一发,问题转化为给你一个数组a,求a中哪两个数的抑或值最大。解决该问题的方法就是Trie树。
对每个权值由二进制高位到低位插到Trie树里,当要询问对于权值x最大的xor的时候,就需要从树上贪心的去匹配,譬如x的高位是1,那么我们就希望从Trie树上往0走,否则的话我们希望往1走,因为1^0=1,0^0=0,1^1=1 ,所有不同的异或才尽可能最大。并且从高位开始异或匹配才尽可能的大,这个在Trie树中是很容易实现的。在本题中n个数,最大是2^31-1,所以需要的节点的数量最多可以到到达32n。我们做的时候是询问一个数,插一个数,每次询问的复杂度也是O(32),所以总复杂度是O(32n);
#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
#include<cmath>
#include<cstdlib>
#include<stack>
#include<queue>
#include <iomanip>
#include<iostream>
#include<algorithm>
using namespace std ;
const int N=200010 ;
struct node
{
int to,w,next;
}edge[N];
struct Trienode
{
Trienode *next[2] ;
}T[N*32],*root ;
int top ,cnt ,head[N],bit[32],cost[N];
void add(int u ,int v, int w )
{
edge[top].to =v ;
edge[top].w=w;
edge[top].next=head[u] ;
head[u]=top++;
edge[top].to=u;
edge[top].w=w;
edge[top].next=head[v];
head[v]=top++;
}
void dfs(int u,int num,int fa)
{
cost[u]=num;
for(int i=head[u] ; i!=-1;i=edge[i].next)
{
int v = edge[i].to ;
if(v!=fa)
{
dfs(v,num^edge[i].w,u);
}
}
}
void insert(int x)
{
Trienode *p=root ;
for(int i = 30 ; i>= 0 ; i--)//从高位开始插进去
{
int id= x&bit[i] ? 1:0 ;//找出插入的数的每一位是1还是0 ;
if(p->next[id] != NULL)//有点了,继续
{
p=p->next[id] ;
}
else
{
p->next[id]=&T[cnt];//创建新节点
T[cnt].next[0]=T[cnt].next[1]=NULL ;//初始化
cnt++;
p=p->next[id] ;
}
}
}
int find(int x)
{
Trienode *p=root ;
int ans=0;
for(int i = 30 ; i >= 0 ; i--)
{
int id= x&bit[i] ? 1:0 ;
if(p->next[id^1]!=NULL)//贪心策略,1^0=1
{
ans += bit[i] ; //异或后的这一位,把结果加上
p=p->next[id^1];
}
else
p=p->next[id] ;
}
return ans ;
}
int main()
{
bit[0]=1; // 二进制每位的数 2^i ;
for(int i = 1 ; i < 32 ; i++)
bit[i]=bit[i-1]*2 ;
int n ;
while(cin>>n)
{
int u,v,w ;
memset(head,-1,sizeof(head)) ;
top=0;cnt=0;
for(int i = 1 ; i < n ; i++)
{
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
}
dfs(0,0,-1);//深搜,算出所有节点到根节点的异或之和
root = &T[cnt] ;//字典树的根节点
T[cnt].next[0]=T[cnt].next[1]=NULL ;//两个子节点初始化(代表0,1节点)
cnt++;
insert(cost[0]) ;//先插入第一个节点;
int ans = 0 ;
for(int i = 1 ; i< n ; i++)
{
int tmp = find(cost[i]) ;//查询该数与之前插入的数的最大的异或和,
ans = ans > tmp ? ans:tmp ; //更新最大异或和
insert(cost[i]); //查询完插入该数
}
printf("%d\n",ans);
}
return 0;
}