最长异或路径(Tire树+贪心)

最长异或路径(Tire树+贪心)

题目描述

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

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

输入格式

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

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

输出格式

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

输入输出样例

输入 #1

4
1 2 3
2 3 4
2 4 6

输出 #1

7
解题思路

首先,异或有一个性质

A ⨁ A = 0 ⇒ A ⨁ B ⨁ A = B A \bigoplus A = 0 \Rightarrow A \bigoplus B \bigoplus A = B AA=0ABA=B

要求两个节点之间的路径上所有的边权异或和,可以先预先处理出每个节点到根节点的所有边权的异或和。

根据如上性质,在计算两个节点的异或和时等价于求两个节点到根节点所有边权的异或和做异或,可以消除掉多计算的边对结果产生的影响。

首先可以用dfs预处理出到根节点的异或和存储在数组中。树上两节点之间的路径唯一,所以随便选取一个根节点即可。

现在求解两个路径异或和的最大值即求两个节点a , b ;

r e s = m a x ( r e s , s u m [ a ] ⨁ s u m [ b ] ) res = max(res , sum[a]\bigoplus sum[b] ) res=max(res,sum[a]sum[b])

用 Tire树+贪心 解决可以这个问题

根据二进制从高到低给每个 sum[i] 建立 Tire 树 , 枚举每个i按二进制求答案,优先考虑答案在当前位为1的情况(即存在 s o n [ p ] [ t m p ⨁ 1 ] son[p][tmp\bigoplus 1] son[p][tmp1]) ,这样可以尽量使res最大。

最后全局的res最大值即为答案

Code
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 4e6 + 10 ;
typedef long long ll ;
int son[N][2], sum[N] , idx;

int h[N] , e[N] , ne[N] , cnt; 
int w[N<<1] ;
void add(int a,int b,int c){
    e[cnt] = b , ne[cnt] = h[a] , w[cnt] = c,  h[a] = cnt++  ; 
}

void dfs(int u , int fa , int x ){
    sum[u] = x; 
    for(int i = h[u] ; ~i ; i = ne[i] ){
        int v = e[i] ;
        if( v == fa ) continue ;
        dfs(v,u,x^w[i]) ; 
    }
}

void build(int n){
    for(int i = 1 ; i <= n ; i ++ ) {
        int x = sum[i] , p = 0 ; 
        for(int j = 30 ; j >= 0 ; j -- ){
            int u = x >> j & 1 ;
            if(!son[p][u]) son[p][u] = ++ idx; 
            p = son[p][u] ; 
        }
    }
}

int query(int n){
    int p = 0 , res = 0 ;
    for(int i = 30 ; i >= 0 ; i -- ){
        int tmp = n >> i & 1 ;
        if(son[p][tmp^1]) {
            res += 1 << i ; 
            p = son[p][tmp^1] ;
        }
        else p = son[p][tmp] ; 
    }
    return res; 
}

int main(){
    #ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
    #endif
    memset(h,-1,sizeof(h)) ;
    int n ; 
    scanf("%d",&n) ; 
    for(int i = 1 ; i < n ; i ++ ){
        int a , b , c ; 
        cin >> a >> b >> c; 
        add(a,b,c) , add(b,a,c) ; 
    }
    dfs(1,-1,0);
    build(n); 
    int res =0 ; 
    for(int i = 1 ; i <= n ; i ++ ) res = max(res,query(sum[i])) ;
    cout << res << endl; 
    return 0 ; 
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值