Evanyou Blog 彩带

P4551 最长异或路径

题目描述

给定一棵 nnn 个点的带权树,结点下标从 111 开始到 NNN 。寻找树中找两个结点,求最长的异或路径。

异或路径指的是指两个结点之间唯一路径上的所有边权的异或。

输入输出格式

输入格式:

第一行一个整数 NNN ,表示点数。

接下来 n−1n-1n1 行,给出 u,v,wu,v,wu,v,w ,分别表示树上的 uuu 点和 vvv 点有连边,边的权值是 www 。

输出格式:

一行,一个整数表示答案。

输入输出样例

输入样例#1: 
4
1 2 3
2 3 4
2 4 6
输出样例#1: 
7

说明

最长异或序列是1-2-3,答案是 7 (=3 ⊕ 4)

数据范围

1≤n≤100000;0<u,v≤n;0≤w<2311\le n \le 100000;0 < u,v \le n;0 \le w < 2^{31}1n100000;0<u,vn;0w<231

 

Solution:

  套路题,01trie树+dfs序。

  首先很容易想到树上任意两点路径的异或和在dfs序上直接可以体现出来,于是直接搞出dfs序,然后求出异或前缀和并插入trie树中,最后只需要在trie树中贪心的求一遍每个异或前缀和能与树中异或和异或出的最大值就好了。

代码:

 

#include<bits/stdc++.h>
#define il inline
#define ll long long
#define For(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
#define Bor(i,a,b) for(int (i)=(b);(i)>=(a);(i)--)
using namespace std;
const int N=2e5+7;
int n,a[N],to[N],net[N],w[N],h[N],cnt,op[N];
int trie[N*31][2],tot,ans;

il int gi(){
    int a=0;char x=getchar();bool f=0;
    while((x<'0'||x>'9')&&x!='-')x=getchar();
    if(x=='-')x=getchar(),f=1;
    while(x>='0'&&x<='9')a=(a<<3)+(a<<1)+(x^48),x=getchar();
    return f?-a:a;
}

il void add(int u,int v,int c){to[++cnt]=v,net[cnt]=h[u],w[cnt]=c,h[u]=cnt;}

il void dfs(int u,int lst,int val){
    a[++cnt]=u,op[u]=val;
    for(int i=h[u];i;i=net[i]) if(to[i]!=lst) dfs(to[i],u,w[i]);
    a[++cnt]=u;
}

il void insert(int a){
    int p=0,x;
    Bor(i,0,30){
        x=(1<<i&a?1:0);
        if(!trie[p][x])trie[p][x]=++tot;
        p=trie[p][x];
    }
}

il int search(int a){
    int p=0,x,ans=0;
    Bor(i,0,30){
        x=(1<<i&a?0:1);
        if(!trie[p][x]) p=trie[p][x^1];
        else p=trie[p][x],ans+=1<<i;
    }
    return ans;
}

int main(){
    n=gi();
    int u,v,c;
    For(i,1,n-1) u=gi(),v=gi(),c=gi(),add(u,v,c),add(v,u,c);
    cnt=0;
    dfs(1,-1,0);
    int sum=0;
    For(i,1,cnt) sum^=op[a[i]],insert(sum);
    sum=0;
    For(i,1,cnt) sum^=op[a[i]],ans=max(ans,search(sum));
    cout<<ans;
    return 0;
}

 

posted @ 2018-08-15 21:31  five20  阅读(222)  评论(0编辑  收藏  举报
Live2D