POJ3746 The xor-longest Path(字典树与链式前向星存图)

题目链接:http://poj.org/problem?id=3764

The xor-longest Path

Time Limit: 2000MS Memory Limit: 65536K

Description

In an edge-weighted tree, the xor-length of a path p is defined as the xor sum of the weights of edges on p:

_{xor}length(p)=\oplus_{e \in p}w(e)

⊕ is the xor operator.

We say a path the xor-longest path if it has the largest xor-length. Given an edge-weighted tree with n nodes, can you find the xor-longest path?  

Input

The input contains several test cases. The first line of each test case contains an integer n(1<=n<=100000), The following n-1 lines each contains three integers u(0 <= u < n),v(0 <= v < n),w(0 <= w < 2^31), which means there is an edge between node u and v of length w.

Output

For each test case output the xor-length of the xor-longest path.

Sample Input

4
0 1 3
1 2 4
1 3 6

Sample Output

7

Hint

The xor-longest path is 0->1->2, which has length 7 (=3 ⊕ 4) 

题意:

       给你一棵n个节点n-1条边的树,每个边都有一个边权,从中选出任意两个节点使这两个节点间的所有边权的异或值最大。

思路:

       设D[x]为根节点到x的路径上的所有边权的异或值,显然D[x]=D[father[x]] xor weight(x,father[x])。根据上式先进行一次深度优先搜索,求出所有的D[x]。设有两节点x和y,根据异或性质 a xor a=0,x到根与y到根的路径上的所有重叠部分恰好抵消,所以x到y的路径上的所有边权的异或值等于D[x] xor D[y].

        所以问题转变为从D[0]--D[N-1]这N个数中选出两个使其异或和最大,用0-1字典树解决即可。

注意 

     (1)存图时用链式前向星,因为多组输入,每次初始化清空数组时,用邻接表可能会超时。链式前向星具体见:https://blog.csdn.net/qq_30331643/article/details/68621435.

      (2)字典树见:https://blog.csdn.net/fire_to_cheat_/article/details/79616679

代码:

#include<iostream>
#include<cstring>
#include<stdio.h>
using namespace std;
const int maxn=1e5+5;
struct edge{
    int to,w,next;
}e[maxn*2];
int head[maxn],cnt=0;
int trie[maxn*32][2],a[maxn],n,MAX=0,tot=0;
void init()
{
    memset(trie,0,sizeof trie);
    memset(a,0,sizeof a);
    memset(e,0,sizeof e);
    memset(head,-1,sizeof head);
    MAX=0;
    tot=0;
    cnt=0;
}
void insert(int x)
{
    int rt=0;
    for(int i=30;i>=0;i--)
    {
        int t=(x>>i)&1;
        if(!trie[rt][t])
            trie[rt][t]=++tot;
        rt=trie[rt][t];
    }
    return;
}
int query(int x)
{
    int res=0,rt=0;
    for(int i=30;i>=0;i--)
    {
        int t=(x>>i)&1;
        if(trie[rt][t^1])
        {
            res|=(1<<i);
            t=t^1;
        }
        rt=trie[rt][t];
    }
    return res;
}
void dfs(int v,int f,int s)
{
    a[v]=s;
    for(int i=head[v];~i;i=e[i].next)
    {
        if(e[i].to!=f)
        {
            dfs(e[i].to,v,s^e[i].w);
        }
    }
}
void addedge(int v,int u,int w)
{
    e[cnt].to=u;
    e[cnt].w=w;
    e[cnt].next=head[v];
    head[v]=cnt++;
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        init();
        for(int i=0;i<n-1;i++)
        {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            addedge(x,y,z);
            addedge(y,x,z);
        }
        dfs(0,0,0);
        for(int i=0;i<n;i++)
        {
            MAX=max(MAX,query(a[i]));
            insert(a[i]);
        }
        cout<<MAX<<endl;
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值