给定一颗N个节点的树,树上的每条边都有一个权值。从树中选择两个点x和y,把x到y的路径上的所有边权xor起来,得到的结果最大是多少?
我们知道x到y的最短路是d[x]+d[y]-2*d[rt],其中rt是最近公共祖先的点。同样我们可以对树进行一次DFS,求出所有d[x]表示根到x的xor值。与最短路不同的是x到y的xor值是d[x]^d[y],因为d[x]^d[y]^d[rt]^d[rt]=d[x]^d[y]。
所以问题就变成了从d[0]~d[n-1]这n个数中选出两个点,使得异或的结果最大。可以用字典树求解。把每个整数看成是长度32的二进制01串,把数列插入字典树中。对于每次检索,尽量走与Ai当前位相反的字符指针,但如果没有这个相反的指针,那么只好访问相同的字符指针。可以知道这样的结果异或最优。
注意这题字典树SIZE的选择,SIZE是p的上限,而p是节点的个数,这个节点数的上限是100000(个最多不同的数)×31(每个数的节点个数)。
代码如下:
#include <iostream> #include <stdio.h> #include <string.h> using namespace std; const int maxn=233333; int head[maxn],nxt[maxn],to[maxn],val[maxn]; int tot; int d[maxn]; int x,y,z,n; int tto,trie[100000*31][2]; void init() { tot=0; memset(head,-1,sizeof head); tto=1; memset(trie,0,sizeof trie); } void add(int x,int y,int z) { to[tot]=y; val[tot]=z; nxt[tot]=head[x]; head[x]=tot++; } void dfs(int x,int fa) { for(int i=head[x]; ~i; i=nxt[i]) { if(to[i]==fa) continue; d[to[i]]=d[x]^val[i]; dfs(to[i],x); } } int search(int x) { int k=31,tx=0,p=1; int ret=0; for(; ~k; --k) { tx=x>>k&1; if(trie[p][!tx]==0) { ret|=tx<<k; p=trie[p][tx]; } else { ret|=(!tx)<<k; p=trie[p][!tx]; } } return ret; } void insert(int x) { int k=31,tx=0,p=1; for(; ~k; --k) { tx=x>>k&1; if(trie[p][tx]==0) trie[p][tx]=++tto; p=trie[p][tx]; } } int main() { while(~scanf("%d",&n)) { init(); for(int i=1; i<n; ++i) { scanf("%d%d%d",&x,&y,&z); add(x,y,z); add(y,x,z); } dfs(0,-1); /* for(int i=0;i<n;++i) printf("%d ",d[i]); puts(""); */ int ans=0; insert(d[0]); for(int i=1; i<n; ++i) { ans=max(ans,d[i]^search(d[i])); insert(d[i]); } printf("%d\n",ans); } return 0; }