给出一棵有n个节点的树,设(w[i][j])为i,j之间的边权,定义一条路径的距离,为这条路径上所有边权的异或和,请你选出任意两个点,最小化这两点之间的路径距离,并求出这个最大值,(1≤n≤100000)。
解
树的问题,无根变有根,更加好研究,通常维护出每个节点到根节点的信息,于是设(s[i])表示点i到根节点的路径的权值的异或和,这个一遍dfs就可以维护出来,我不再赘诉了。
此时发现对于任意两个点i,j之间的距离就变成(w[i]wedge w[j]),原因在于相同的数异或相同的数结果为0,而(w[i]wedge w[j]),恰好因为这个性质,除去了i,j最近公共祖先到根节点的距离,剩下的距离自然都是i,j的距离了。
于是现在问题变成从集合({w[i]})选出两个数,最大化它们的异或和,显然我们只要维护一棵trie树,将这些数当做01串从高位到低位插入trie树,然后对于每个数从trie树从上往下尽量选择让该数的这一位不为0的数字即可。
参考代码:
#include <iostream>
#include <cstdio>
#define il inline
#define ri register
#define Size 100050
using namespace std;
struct point{
point *next;int to,w;
}*pt,*head[Size];
bool is[Size];
int dis[Size],trie[Size*31][2],tot;
void dfs(int);
il int ask(int);
il void read(int&),
link(int,int,int),
insert(int);
template<class free>
il free Max(free,free);
int main(){
int n,ans(0);read(n);
for(int i(1),u,v,w;i<n;++i)
read(u),read(v),read(w),
link(u,v,w),link(v,u,w);dfs(0);
for(int i(1);i<=n;++i)
ans=Max(ans,ask(dis[i]));
printf("%d",ans);
return 0;
}
template<class free>
il free Max(free a,free b){
return a>b?a:b;
}
il int ask(int x){
int ans(0),p(0);
for(int i(30);i>=0;--i)
if(trie[p][x>>i&1^1])
p=trie[p][x>>i&1^1],
ans|=1<<i;
else p=trie[p][x>>i&1];
return ans;
}
il void insert(int x){int p(0);
for(int i(30);i>=0;--i){
if(!trie[p][x>>i&1])
trie[p][x>>i&1]=++tot;
p=trie[p][x>>i&1];
}
}
void dfs(int x){is[x]|=true,insert(dis[x]);
for(point *i(head[x]);i!=NULL;i=i->next){
if(is[i->to])continue;
dis[i->to]=dis[x]^i->w,dfs(i->to);
}
}
il void link(int u,int v,int w){
pt=new point{head[u],v,w},head[u]=pt;
}
il void read(int &x){
x^=x;ri char c;while(c=getchar(),c<'0'||c>'9');
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
}