题意
给定一棵 (n) 个点的带权树,结点下标从 (1) 开始到 (N) 。寻找树中找两个结点,求最长的异或路径。
异或路径指的是指两个结点之间唯一路径上的所有边权的异或。
解法
-
先预处理出从根结点到所有结点的路径上的 (xor) 值,显然 (d(x) = d(fa)) ^ (val(fa o x))。
-
又因为异或有一性质 (a) ^ (a=0) ,所以可以发现从 (x) 到 (y) 的异或路径就是 (d(x)) ^ (d(y)),因为 (d(1)) ^ (d(x-1)) 经过两次计算相抵消了。
-
然后问题就转化了在 (1 le i,jle n) 中找到最大的 (d(i)) ^ (d(j)),把每个数看做一个二进制01串,建立一棵01字典树,利用贪心的思想每次找与当前位相反的结点即可。
代码
#include <iostream>
#include <algorithm>
#define M 200070
#define N 5000070
using namespace std;
struct Edge
{
int to,nxt,w;
}e[M];
int n,ans,cnt,tot;
int d[M],head[M],nxt[N][2];
void add(int from,int to,int val){
e[++cnt].to=to;
e[cnt].w=val;
e[cnt].nxt=head[from];
head[from]=cnt;
}
void dfs(int x,int fa){
for(int i=head[x];i;i=e[i].nxt){
int v=e[i].to;
if(v==fa) continue;
d[v]=d[x]^e[i].w;
dfs(v,x);
}
}
void insert(int x){
int c=0,op;
for(int i=31;i>=0;--i){
op=((x>>i)&1);
if(!nxt[c][op])
nxt[c][op]=++tot;
c=nxt[c][op];
}
}
int query(int x){
int c=0,sum=0,op;
for(int i=31;i>=0;--i){
op=((x>>i)&1);
if(nxt[c][op^1])
c=nxt[c][op^1],sum=sum<<1|1;
else
c=nxt[c][op],sum=sum<<1;
}
return sum;
}
int main(){
cin>>n;
for(int i=1;i<n;++i){
int u,v,w;
cin>>u>>v>>w;
add(u,v,w);
add(v,u,w);
}
dfs(1,0);
insert(d[1]);
for(int i=2;i<=n;++i){
ans=max(ans,query(d[i]));
insert(d[i]);
}
cout<<ans<<endl;
}
时间复杂度为(Theta (31*N))。