最长异或值路径
给定一个树,树上的边都具有权值。
树中一条路径的异或长度被定义为路径上所有边的权值的异或和:
⊕ 为异或符号。
给定上述的具有n个节点的树,你能找到异或长度最大的路径吗?
输入格式
第一行包含整数n,表示树的节点数目。
接下来n-1行,每行包括三个整数u,v,w,表示节点u和节点v之间有一条边权重为w。
输出格式
输出一个整数,表示异或长度最大的路径的最大异或和。
数据范围
1≤n≤100000,
0≤u,v<n,
0≤w<231
输入样例:
4
0 1 3
1 2 4
1 3 6
输出样例:
7
样例解释
样例中最长异或值路径应为0->1->2,值为7 (=3 ⊕ 4)
从图中我们可以得到异或路径值的规律,任意两个节点的路径值和它们分别到根节点的路径值在进行异或运算得到的就是两节点之间的异或路径值;现在我们首先要做的就是求出每个节点到根节点的异或路径值,然后在利用Tire求出所有异或值中两个异或值最大的值,就是异或值最大的一条路径
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=100010,M=200010;
int h[N],e[M],c[M],ne[M],cnt,n;
//h[i]数组表示的是i节点指向其他节点的路径中排序最大的那一个(最后输入的那一个)
//e[i]第i条路径有指向的下一个节点,这里有u->v (i)和v->u (j)
//是两条不同的路径,所以这里e[i]=v,e[j]=u
//c[i] 表示的是第i条路径的权值
//ne[i]表示在路径排序中第i条路径的上一条路径
//其中h数组的大小与节点的个数有关
//而e、c、ne数组都是与单一路径成2倍关系
//cnt为路径的排序
int a[N],son[3100010][2],idx;
//我们通过dfs搜索将每个节点到根节点的路径异或值计算出来到a数组中
//每一个数据最多占用二进制31位所以
//son数组最大位3100010
void add(int u,int v,int w)
{ //建立两节点的单向路径
//每次调用该函数时,就表示当前是cnt条路径
e[cnt]=v;//当前路径的指向节点
c[cnt]=w;//当前路径的权值
ne[cnt]=h[u];//当前路径的上一条路径
h[u]=cnt++;//使下次调用时路径排序加1
}
void dfs(int u,int father,int sum)
{
a[u]=sum;//u节点到根节点的异或路径值位sum
for(int i=h[u];~i;i=ne[i])
{//当i节点一直循环遍历ne数组的话
//表示的是当前节点所有链接的路径中从高到低一直遍历
//最后遍历到上一个节点的路径,即最小的哪一个
int j=e[i];
if(j!=father)dfs(j,u,sum^c[i]);
}
}
这里我们解释一下dfs中循环中的内容:
void insert(int x)
{
int p=0;
for(int i=30;i>=0;i--)
{//将x的二进制写入son数组中
int s=(x>>i)&1;
if(!son[p][s])son[p][s]=++idx;
p=son[p][s];
}
}
int search(int x)
{
int p=0;
int res=0;
for(int i=30;i>=0;i--){
int s=(x>>i)&1;
if(son[p][!s])
{
p=son[p][!s];
res+=1<<i;
}
else p=son[p][s];
}
return res;
}
int main()
{
ios::sync_with_stdio(false);
memset(h,-1,sizeof h);
cin>>n;
for(int i=0;i<n-1;i++)
{
int u,v,w;
cin>>u>>v>>w;
add(u,v,w);
//u->v
add(v,u,w);
//v->u
}
dfs(0,-1,0);
//我们本身将h数组的每一个数组初始化位-1,即根节点
//我们通过dfs搜索从0~-1到1~-1、2~-1…… 得到每个节点到根节点的异或路径值
//现在我们需要解决的是在得到a数组的异或值中寻找一对异或值最高的结果
for(int i=0;i<n;i++)
insert(a[i]);
int res=0;
for(int i=0;i<n;i++)
res=max(res,search(a[i]));
cout<<res<<endl;
return 0;
}