poj 3764 最长异或路径(二进制trie树)

【问题描述】

  给你一棵树,n个节点,n-1条边每条边i都有一个权值wi。定义任意两点间的权值为:这两点间的路径上的所有边的值的异或。比如a点和b点间有i,j,k三条边,那么ab两点间的权值为:wi^wj^wk。求这个最大的权值(最长异或路径)。

【输入格式】

  第一行为n表示节点数目(节点编号为1..n)。
  接下来的n-1行,每行三个整数:u v w,表示一条树边(x,y)的权值为w(0<=w<2^31)。

【输出格式】

  输出最长异或路径长度。

【输入样例】

4
1 2 3
2 3 4
2 4 6

【输出样例】

【样例解释】

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

【数据范围】

n<=250000

【来源】

poj 3764

这道题都能想到用dfs来生成根到每一个点的异或路径,但生成之后的操作就是重点了。
首先我们可以很容易的想到任意2个点直接的异或路径就是他们到跟的异或路径的异或值,证明如下:
设2点为x,y,公共祖先是z。z到根的异或路径是c,x到z的异或路径是a,y到z的异或路径是b。可得a^b=a^c^b^c。
不用二进制trie树的话很容易想到一个n^2时间复杂的算法,就是每2个数进行异或。但如果有了二进制trie树就可以先生成树,在再树上贪心的进行查找,很容易就可以得到最大值了,时间复杂度(n*log2n)。

#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
using namespace std;
const int maxn=250005;

struct edge
{
    int u,v,w,next;
}e[maxn];
int d[maxn],f[maxn]={0},ch[maxn*33][2]={0},cnt=0,tot=0,n;
int a[33];
bool vis[maxn*33]={0},usd[maxn]={0};

void in(int x)
{
    int k=30,p=0,d;
    while(k>=0)
    {
        if(x&a[k]) d=1;
        else d=0;
        if(!ch[p][d]) ch[p][d]=++cnt;
        p=ch[p][d];
        k--;
    }
    vis[p]=1;
}
void add(int u,int v,int w)
{
    e[++tot]=(edge){u,v,w,f[u]};f[u]=tot;
}
int read()
{
    int x=0;
    char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x;
}
void dfs(int i)
{
    for(int k=f[i];k;k=e[k].next)
    {
        int j=e[k].v,c=e[k].w;
        d[j]=d[i]^c;
        in(d[j]);
        dfs(j);
    }
}
int find(int x)
{
    int k=30,p=0,d,y=0;
    while(k>=0)
    {
        if(x&a[k]) d=0;
        else d=1;
        if(!ch[p][d]) d=d^1;
        if(d) x^=a[k];
        p=ch[p][d];
        k--;
    }
    return x;
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    n=read();
    a[0]=1;
    for(int i=1;i<=30;i++) a[i]=a[i-1]*2;
    int x,y,w;
    for(int i=1;i<n;i++)
    {
        x=read();y=read();w=read();
        add(x,y,w);
        usd[y]=1;
    }
    in(0);
    for(int i=1;i<=n;i++)if(!usd[i])
    dfs(i);
    int ans=0;
    for(int i=1;i<=n;i++)
    ans=max(ans,find(d[i]));

    cout<<ans;
    return 0;
}
    原文作者:Trie树
    原文地址: https://blog.csdn.net/qq_35546274/article/details/60966361
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞