字典树的题目,开始看的时候毫无头绪,不知道怎么下手,看了别人的解题博客后终于明白了怎么做。这道题目出的很巧妙。之前比赛的时候遇到过一道类似的题目,不过当时我们没做出来,我们队伍还比较弱,还要奋斗啊。大致思路:由于以下式子的成立:(a^b)^(b^c) = c ,所以我们可以这样来建树,每个节点保存从根节点走到当前节点的异或值。然后我们建立一个01字典树。逐个插入每个节点的值(以二进制位方式插入),在插入之前我们先查找一个值,使得这个值是当前已插入字典树的数值中与带插入的值异或最大的。在查询字典树中该数值的时候,我们尽可能的与当前带插入的数值的对应二进制位相反的方向走,这样异或后得到的值肯定会比较大。取出其中比较大的那个异或结果即可。
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std ;
#define maxn 200100
struct Trie{
int next[2] ;
int cnt ;
}trie[maxn<<3] ;
int first[maxn] ;//存放节点u的第一条边的编号
int u[maxn] ;
int v[maxn] ;
int w[maxn] ;
int next[maxn] ;//存放下一条边的编号
int ans ;
int n ;
int ncount ;
void add_edge(int x , int y , int z , int e){
next[e] = first[x] ;
first[x] = e ;
u[e] = x ;
v[e] = y ;
w[e] = z ;
}
void init(){
memset(first , -1 , sizeof (first) ) ;
memset(next , -1 , sizeof (next) ) ;
memset(u , 0 , sizeof(u)) ;
memset(v , 0 , sizeof(v)) ;
memset(w , 0 , sizeof(w)) ;
memset(trie , -1 , sizeof (trie) ) ;
ncount = 0 ;
ans = 0 ;
}
void cal(int x , int site ,int cnt){
if(cnt == -1){
int y = x ^ trie[site].cnt ;
if(y > ans)
ans = y ;
}
else if(trie[site].next[ (~(x>>cnt)) & 1] != -1){
cal(x , trie[site].next[ (~(x>>cnt)) &1 ] , cnt - 1) ;
}
else
cal(x , trie[site].next[ (x>>cnt) & 1 ] , cnt - 1 ) ;
}
void insert(int x , int site , int cnt){
if(cnt != -1){
if(trie[site].next[ (x>>cnt) & 1 ] == -1){
trie[site].next[(x>>cnt) & 1] = ++ncount ;
}
insert(x , trie[site].next[(x>>cnt) &1] , cnt-1) ;
}
else{
trie[site].cnt = x ;
}
}
void make(int x ){
cal(x , 0 , 31) ;
insert(x , 0 , 31) ;
}
void build_tree(int e , int fa ,int last){
make(last) ;
while(e != -1){
if(v[e] != fa){
build_tree(first[ v[e] ] , u[e] , last ^ w[e]) ;
}
e = next[e] ;
}
}
bool read(){
if(scanf("%d" , &n) == EOF){
return 0 ;
}
int x ;
int y ;
int z ;
init() ;
for(int e = 1 ; e < n ; e ++){
scanf("%d%d%d" , &x , &y , &z) ;
add_edge(x , y , z , e) ;
add_edge(y , x , z , e + n) ;
}
return 1 ;
}
void solve(){
//建树
build_tree(first[ u[1] ] , -1 , 0) ;
printf("%d\n" , ans) ;
}
int main(){
while(read()){
solve() ;
}
}