题目描述
给定一棵 nn 个点的带权树,结点下标从 11 开始到 nn。寻找树中找两个结点,求最长的异或路径。
异或路径指的是指两个结点之间唯一路径上的所有边权的异或。
输入格式
第一行一个整数 nn,表示点数。
接下来 n-1n−1 行,给出 u,v,wu,v,w ,分别表示树上的 uu 点和 vv 点有连边,边的权值是 ww。
输出格式
一行,一个整数表示答案。
输入输出样例
输入 #1复制
4 1 2 3 2 3 4 2 4 6
输出 #1复制
7
说明/提示
最长异或序列是 1,2,31,2,3,答案是 7=3\oplus 47=3⊕4。
数据范围
1\le n \le 100000;0 < u,v \le n;0 \le w < 2^{31}1≤n≤100000;0<u,v≤n;0≤w<231。
分析: 一个数 异或同一个数两次 值不变
所以a到b的异或和 可以处理为a到1的异或和再异或b到1的异或和
(异或运算满足结合律和交换律)
#include<bits/stdc++.h>
int trie[50000000][2];
int sum[1000009];
int n;
int tot;
int check[5000009];
struct node{
int v;
int w;
int next;
};
node edge[1000009];
int head[1000009];
int cnt;
void add(int x,int y,int z){ //链式前向星存储
edge[cnt].v=y;
edge[cnt].w=z;
edge[cnt].next=head[x];
head[x]=cnt++;
}
void dfs(int cur,int fa){ //dfs预处理每个节点到根节点1的路径异或和
int i;
for(i=head[cur];~i;i=edge[i].next){ //~相当于 取相反数再-1
if(edge[i].v!=fa){
sum[edge[i].v]=sum[cur]^edge[i].w;
dfs(edge[i].v,cur);
}
}
return ;
}
void insert(int num){
int p=0;
int i;
for(i=30;i>=0;i--){
int op=(num>>i)&1;
if(trie[p][op]==0){
trie[p][op]=tot;
tot++;
}
p=trie[p][op];
}
check[p]=num;
return ;
}
int find(int num){
int p=0;
int i;
for(i=30;i>=0;i--){
int op=(num>>i)&1;
if(trie[p][op^1]){
p=trie[p][op^1];
}else{
p=trie[p][op];
}
}
return check[p];
}
int main (){
memset(head,-1,sizeof(head)); //注意预处理为-1
scanf("%d",&n);
int i;
for(i=1;i<n;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
dfs(1,-1);
tot=1;
for(i=1;i<=n;i++){
insert(sum[i]);
}
int ans=0;
for(i=1;i<=n;i++){
ans=std::max(ans,sum[i]^find(sum[i])); //找到后别忘了再异或
}
printf("%d\n",ans);
return 0;
}
/*
*
*
*
**/