poj3764(dfs+Trie树+贪心)

本文深入分析了一道与树形结构相关的异或最大值问题,提出使用字典树+贪心优化的方法解决。通过预处理每个节点到根节点的异或和并插入字典树,实现快速查找异或值最大化的两个节点,最终得到复杂度为(n*30)的高效算法。

 

题目链接: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

 

转载于:https://www.cnblogs.com/lienus/p/4133150.html

乐播投屏是一款简单好用、功能强大的专业投屏软件,支持手机投屏电视、手机投电脑、电脑投电视等多种投屏方式。 多端兼容与跨网投屏:支持手机、平板、电脑等多种设备之间的自由组合投屏,且无需连接 WiFi,通过跨屏技术打破网络限制,扫一扫即可投屏。 广泛的应用支持:支持 10000+APP 投屏,包括综合视频、网盘与浏览器、美韩剧、斗鱼、虎牙等直播平台,还能将央视、湖南卫视等各大卫视的直播内容一键投屏。 高清流畅投屏体验:腾讯独家智能音画调校技术,支持 4K 高清画质、240Hz 超高帧率,低延迟不卡顿,能为用户提供更高清、流畅的视觉享受。 会议办公功能强大:拥有全球唯一的 “超级投屏空间”,扫码即投,无需安装。支持多人共享投屏、远程协作批注,PPT、Excel、视频等文件都能流畅展示,还具备企业级安全加密,保障会议资料不泄露。 多人互动功能:支持多人投屏,邀请好友加入投屏互动,远程也可加入。同时具备一屏多显、语音互动功能,支持多人连麦,实时语音交流。 文件支持全面:支持 PPT、PDF、Word、Excel 等办公文件,以及视频、图片等多种类型文件的投屏,还支持网盘直投,无需下载和转格式。 特色功能丰富:投屏时可同步录制投屏画面,部分版本还支持通过触控屏或电视端外接鼠标反控电脑,以及在投屏过程中用画笔实时标注等功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值