题目描述
给定一棵n个点的带权树,结点下标从1开始到N。寻找树中找两个结点,求最长的异或路径。
异或路径指的是指两个结点之间唯一路径上的所有边权的异或。
输入格式
第一行一个整数N,表示点数。
接下来 n−1 行,给出 u,v,w ,分别表示树上的 u 点和 v 点有连边,边的权值是 w。
输出格式
一行,一个整数表示答案。
输入输出样例
输入 #1
4
1 2 3
2 3 4
2 4 6
输出 #1
7
说明/提示
最长异或序列是1-2-3,答案是 7 (=3 ⊕ 4)
数据范围
1≤n≤100000;0<u,v≤n;0≤w<231
解释:首先利用异或的性质, a ⊕ a = 0 a⊕ a=0 a⊕a=0则我们可以预处理出来点 v v v到树根的异或路径的值, d p [ v ] dp[v] dp[v]
得到 d p [ 1 ] , d p [ 2 ] , . . . , d p [ n ] dp[1],dp[2],...,dp[n] dp[1],dp[2],...,dp[n]后,问题转化成求取两个数异或最大值,很经典的字典树问题,直接上模板
#include<iostream>
#define N 200005
using namespace std;
int head[N]={0};
int nex[N]={0};
int V[N]={0};
int To[N]={0};
int tot=0;
int dp[N]={0};
int n=0;
struct node{
node *tree[2];
int v;
};
void free(node *rt){
if(!rt) return;
free(rt->tree[0]);free(rt->tree[1]);
delete(rt);
}
void insert(node *rt,int v,int num){
if(num>31){
rt->v=1;
return;
}
int y=(v>>(31-num))&1;
if(rt->tree[y]==NULL){
rt->tree[y]=new node;
rt->tree[y]->tree[0]=rt->tree[y]->tree[1]=NULL;
rt->tree[y]->v=0;
}
insert(rt->tree[y],v,num+1);
}
int query(node *rt,int v){
int ret=0;
node *temp=rt;
for(int i=30;i>=0;i--){
int y=(v>>i)&1;
if(temp->tree[!y]){
ret|=(1<<i);
temp=temp->tree[!y];
}else{
temp=temp->tree[y];
}
}
return ret;
}
node *root=new node;
void add(int x,int y,int v){
tot++;
nex[tot]=head[x];
V[tot]=v;To[tot]=y;
head[x]=tot;
}
void dfs(int rt,int fa){
for(int i=head[rt];i;i=nex[i]){
int to=To[i],v=V[i];
if(to==fa) continue;
dp[to]=dp[rt]^v;
dfs(to,rt);
}
}
int main(){
ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<n;i++){
int a,b,c;cin>>a>>b>>c;
add(a,b,c);add(b,a,c);
}
dfs(1,0);
int ret=0;
insert(root,dp[1],1);
for(int i=2;i<=n;i++){
ret=max(ret,query(root,dp[i]));
insert(root,dp[i],1);
}
free(root);
cout<<ret<<endl;
return 0;
}