传送门
题目大意:给定一棵 n 个点的带权树,求树上最长的异或和路径。
对于树上从x到y的路径的异或和,表示为 D ( x , y ) D(x,y) D(x,y),则有:
D ( x , y ) = D ( 1 , x ) ⊕ D ( 1 , y ) D(x,y)=D(1,x) \oplus D(1,y) D(x,y)=D(1,x)⊕D(1,y)
而所有的 D ( 1 , i ) D(1,i) D(1,i)是可以 O ( n ) O(n) O(n)求出的。
问题转化为在N-1个数中找出两个数,使他们异或运算的结果最大。
我们把所有的 D ( 1 , i ) D(1,i) D(1,i)用二进制表示并补足32位,从高位向低位插入Trie树中,在插入之前先求出当前树与前i-1个异或的最大值,即贪心地尽量使同一位上的数不同,没有时才往相同的点走,更新答案。
Code:
#include
using namespace std;
struct edge{
int y,c,next;
}a[200010];int first[100010];
struct node{
int son[2];
node(){
son[0]=son[1]=-1;
}
void clear(){
son[0]=son[1]=-1;
}
}tr[3100010];
int v[100010];
int n,len,x,y,c;
long long ans;
void ins(int x,int y,int c){
a[++len]=(edge){y,c,first[x]};
first[x]=len;
}
void bt(int x)
{
int now=0,p;
for(int i=31;i>=0;i--)
{
if(x&(1<<i)) p=1;
else p=0;
if(tr[now].son[p]==-1)
tr[now].son[p]=++len;
now=tr[now].son[p];
}
}
void cal(int x)
{
int now=0,p;
long long sum=0;
for(int i=31;i>=0;i--)
{
if(x&(1<<i)) p=1;
else p=0;
if(tr[now].son[!p]!=-1)
sum|=(1<<i);
else p=!p;
now=tr[now].son[!p];
}
ans=max(ans,sum);
}
void dfs(int x,int fa,int val)
{
for(int i=first[x];i;i=a[i].next)
{
int y=a[i].y;
if(y==fa) continue;
dfs(y,x,v[y]=val^a[i].c);
}
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
for(int i=0;i<=len;i++)
tr[i].clear();
memset(first,len=0,sizeof first);
for(int i=1;i<n;i++)
{
scanf("%d %d %d",&x,&y,&c);
x++;y++;
ins(x,y,c);ins(y,x,c);
}
len=0;v[1]=0;dfs(1,0,0);
ans=0;bt(v[1]);
for(int i=2;i<=n;i++)
{
cal(v[i]);
bt(v[i]);
}
printf("%lld\n",ans);
}
}