题目链接:https://www.acwing.com/problem/content/146/
题目:
给定一个树,树上的边都具有权值。
树中一条路径的异或长度被定义为路径上所有边的权值的异或和:
⊕为异或符号。
给定上述的具有 n 个节点的树,你能找到异或长度最大的路径吗?
输入格式
第一行包含整数 n,表示树的节点数目。
接下来 n−1 行,每行包括三个整数 u,v,w,表示节点 u 和节点 v 之间有一条边权重为 w。
输出格式
输出一个整数,表示异或长度最大的路径的最大异或和。
数据范围
1≤n≤100000,
0≤u,v<n,
0≤w<2^31输入样例:
4 0 1 3 1 2 4 1 3 6
输出样例:
7
样例解释
样例中最长异或值路径应为
0->1->2
,值为 7(=3⊕4)
思路:
树型题目的一个常用技巧就是:求某个树型结构的两个点之间的路径关系,时常会被转化为点A到根节点的路径 和 点B到根节点的路径之间的关系。
所以这道题,我么可以先建好树型结构,然后随便选定一个节点作为根节点,然后对这个树型结构的所有节点进行dfs,求所有节点到根节点的路径的异或和存入到数组a[]中。
于是数组a[]中存的就是各个点到根节点的路径的异或和,而此题求的是任意两点A,B之间路径的异或和最大值, 所以就可以使用trie树,从a[]中找到异或和的最大值。
通过trie树找到所有值中异或值的最大值:
如给出一组数,3,5,4,6 从中选出两个数求异或的最大值。
暴力的做法是
for(int i = 0 ; ; i++)
{
for(int j = 0 ; j < i ; j++)
{
}
}
但可以使用trie树对第二层for循环进行优化,如取值范围为0~ 2 ^31次方,就可以将第二层for优化到固定31次,使得时间复杂度变为31 * n.
思路:
求3(000....0011)的异或最大值,那么异或的最大情况肯定是,首先第30位(从左往右 位数分别为:30,29,....,0)的值为1时的x,x 与 3的异或可以使得所求值的最高位为1.
同样,第29位 也寻找与 3的第29位不同的情况。
但是如果trie树存储的时候没有这种情况的边,由于2进制表示每一位的值要么为0,要么为1.
所以没有异或最大的情况,就只能往唯一条边方向往下,最终得到的值就是 与3异或最大的值。
举例:
现在已经通过trie树存储3和5的情况(以3位为例),现在找6(110)的最大异或值,
第2位为1,所以找0,于是向左走,第1位为1,所以想要找0,但是当前不存在0的路径,所以往1的方向走,第0位为0,所以找1,于是往1的方向走,所以在3,5,6的情况下,与6异或最大的就是3.
求一组数的最大异或值 参考题目链接:https://www.acwing.com/problem/content/145/
代码实现:
# include <iostream>
# include <cstring>
using namespace std;
const int N = 100010;
int a[N]; // 存的为每个节点到根节点的总路径的异或值
int h[N],e[2 * N],ne[2 * N],w[2 * N],idx;
int p[31 * N + 10][2],cnt;
int n;
void add(int l, int r , int wei)
{
e[idx] = r;
w[idx] = wei;
ne[idx] = h[l];
h[l] = idx++;
}
void dfs(int i , int father , int num)
{
a[i] = num;
for(int j = h[i] ; j != -1 ; j = ne[j])
{
int k = e[j]; //真实的节点
if(k != father)
{
dfs(k,i,num ^ w[j]);
}
}
}
void insert(int x)
{
int temp = 0;
for(int i = 30 ; i >= 0 ; i--)
{
int u = (x >> i) & 1;
if(!p[temp][u])
{
p[temp][u] = ++cnt;
}
temp = p[temp][u];
}
}
int query(int x)
{
int temp = 0;
int val = 0;
for(int i = 30 ; i >= 0 ; i--)
{
int u = (x >> i) & 1;
if(p[temp][!u])
{
val = val * 2 + !u;
temp = p[temp][!u];
}
else
{
val = val * 2 + u;
temp = p[temp][u];
}
}
return val ^ x;
}
int main()
{
scanf("%d",&n);
memset(h,-1,sizeof h);
for(int i = 0 ; i < n ; i++)
{
int l,r,w;
scanf("%d %d %d",&l,&r,&w);
add(l,r,w);
add(r,l,w);
}
dfs(0,-1,0);
int max1 = 0;
//共n个节点
for(int i = 0; i < n ; i++)
{
insert(a[i]);
max1 = max(max1,query(a[i]));
}
printf("%d\n",max1);
return 0;
}