poj3764(dfs+Trie树+贪心)

 

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

 

分析:好题!武森09年的论文中有道题CowXor,求的是线性结构上的,连续序列的异或最大值,用的办法是先预处理出前n项的异或值,然后在这些值中找出两个值的异或值最大。是基于这样的一个原理,相同段的异或值为0。这题在树中找两个节点,两个节点间有唯一路径(因为是树),把路径不断做异或,异或完后求最大的。数据是10万,O(n2)算法超时。我们知道异或有这样的性质:a^b = (a^c)^(b^c),这样就可以考虑找出a与b公共的c,实际上就是求出从根节点到每个节点的异或值,这样任意两个点做异或,即是他们之间的异或路径(相同部分异或抵消了)。实际上又回到了n(n>100000)个数中找两个值的异或值最大。n^2遍历肯定不行了,这里我们用字典树+贪心优化。即建立一棵trie树每层只有0和1;然后将每个点到达根节点的异或和(这里dfs就行)insert进trie树,然后对于每个点从高位往低位一层层在字典树中尽量找与它相异的值(相异为1嘛,且任意每层取一个数形成的二进制数都是之前插进去的),这样找得下来的值至少大于原值,必定能找出最优值,复杂度(n*30)。

 

 

#include <cstdio>

#include <cstring>

#include <cmath>

#include <iostream>

#include <algorithm>

#include <queue>

#include <cstdlib>

#include <vector>

#include <set>

#include <map>

#define LL long long

#define inf 1<<30

#define mod 1000000007

#define N 100010

using namespace std;

struct edge

{

    int v,w,next;

    edge(){}

    edge(int v,int w,int next):v(v),w(w),next(next){}

}e[N<<1];

int head[N],vis[N],nor[N],tot;

void addedge(int u,int v,int w)

{

    e[tot]=edge(v,w,head[u]);

    head[u]=tot++;

}

void dfs(int u,int w)

{

    vis[u]=1;nor[u]=w;

    for(int i=head[u];~i;i=e[i].next)

    {

        int v=e[i].v;

        if(vis[v])continue;

        dfs(v,e[i].w^w);

    }

}

struct Trie

{

    int next[N<<4][2];

    int root,L;

    int newnode()

    {

        next[L][0]=next[L][1]=-1;

        return L++;

    }

    void init()

    {

        L=0;

        root=newnode();

    }

    void insert(int x)

    {

        int now=root;

        for(int i=30;i>=0;i--)

        {

            int num=(1<<i)&x?1:0;

            if(next[now][num]==-1)

                next[now][num]=newnode();

            now=next[now][num];

        }

    }

    int find(int x)

    {

        int now=root,res=0;

        for(int i=30;i>=0;i--)//从二进制的第31位到第1位查找最大异或值

        {

            int num=(1<<i)&x?0:1;//获取num的二进制数的第i+1位上的数字的非

            if(next[now][num]!=-1)//如果字典树有要查找的数即有相异的数

            {

                res|=1<<i;//加上该位的权值

                now=next[now][num];//进入下一层

            }

            else now=next[now][!num];//如果没有,则按num当前位上的数字进入下一层查找

        }

        return res;

    }

}trie;

int main()

{

    int n,u,v,w;

    while(scanf("%d",&n)>0)

    {

        memset(vis,0,sizeof(vis));

        memset(head,-1,sizeof(head));

        tot=0;trie.init();

        for(int i=1;i<n;i++)

        {

            scanf("%d%d%d",&u,&v,&w);

            addedge(u,v,w);

            addedge(v,u,w);

        }

        dfs(0,0);

        int ans=0,mx;

        for(int i=0;i<n;i++)

        {

            trie.insert(nor[i]);

            mx=trie.find(nor[i]);

            ans=max(ans,mx);

        }

        printf("%d\n",ans);

    }

}
View Code

 

你可能感兴趣的:(trie)