您的位置:首页 > 其它

POJ 3764 The xor-longest Path【字典树】

2015-02-25 02:00 417 查看
此乃好

题意:给定从0开始编号的点和两点间的值,对于任意两点,把连接这两点的所有路径上的权值都异或,求某一条路径,它的值最大。

首先存图,肯定要的,需要用邻接表【无向图】(连vector模拟邻接表都超时),由异或规律(a^b)^(b^c)=(b^a)^(b^c)得我们可以先求出所有点到0号点的异或值,这样问题转为在这些值里找两个值,它们异或值最大。如下例:


②到③的异或路径和=①到②的路径和 ^ ①到③的路径和,然后相同的2会异或抵消掉,结果就是4^5;

那么如何在一堆值里求两个数,它们值异或最大?暴力O(n^2)必超时。异或当然要想到二进制了。

于是建立一个深度为31的01字典树,每一位都是0或1,代表一个数的二进制某一位,到最下面叶子就代表了这个数,然后贪心想法,一个数x与y异或要最大的话,X的较高位应该与Y的同位相反(0和1相反),这样才值大,只要这个位异或出1,后面低位的不管是0还是1都不会比这个数大,对于每一位都如此,即沿着树往下找跟他异或最大的那个Y时,应尽量找他相反的,沿着那条路往下,如果该位没有就选相同的。插入每个X来建树和查找Y同步操作,在一个循环里。

每个节点用结构体保存,next[0]:该结点的后继结点有没有0这个数(即低一位有没有0)有的话就保存这个结点的编号,没有就为0,所以结点编号应从1开始。

struct node
{
int next[2];        //保存编号
void init(){
CLR(next,0);   //0表示不存在结点,其他数字为编号
}
}node[maxm];


完整代码:
//8844K	563MS
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
using namespace std;
#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )
#define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define CLR( a , x ) memset ( a , x , sizeof (a) );
#define RE freopen("1.in","r",stdin);
#define WE freopen("1.out","w",stdout);

const int maxm=200100; //这个大小没法估计..理论上最大是2^31次方-1
const int maxn=100025;
struct node { int next[2]; //保存编号 void init(){ CLR(next,0); //0表示不存在结点,其他数字为编号 } }node[maxm];
struct Edge
{
int v,w,next;
}edge[maxn*2];
int cnt;
int head[maxn];
int val[maxn],vis[maxn];
int n;
void addedge(int u,int v,int w) //前插法邻接表
{
edge[cnt].v=v;edge[cnt].w=w;edge[cnt].next=head[u];head[u]=cnt++;
edge[cnt].v=u;edge[cnt].w=w;edge[cnt].next=head[v];head[v]=cnt++;
}

void init_edge()
{
cnt=0;
CLR(head,-1); //for循环的话注意head
也要清
}
void dfs(int u,int w)
{
val[u]=w; //val[u]:0点到u点的异或和
vis[u]=1;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(!vis[v])
dfs(v,w^edge[i].w);
}
}
int tol;
void add(int x)
{
int u=0,idx;
for(int i=30;i>=0;i--)
{
if((1<<i) & x) idx=1;else idx=0; //该位数字
// int idx=((x>>i)&1); //!天真了,以为这个结果不是0就是1
if(!node[u].next[idx]) //不存在就新建
{
node[u].next[idx]=++tol;
node[tol].init();
}
u=node[u].next[idx]; //往下走
}
}
int query(int x)
{
int u=0,res=0,idx;
for(int i=30;i>=0;i--)
{
if((1<<i)&x) idx=1; else idx=0;
if(node[u].next[!idx]) //相反数
{
u=node[u].next[!idx];
res|=(1<<i); //加上路径上的值
}else u=node[u].next[idx];
}
return res; //求x^res最大跟求res最大一样
}

int main()
{
int u,v,w;
// RE
while(scanf("%d",&n)!=EOF)
{
tol=0;
cnt=0;
init_edge();
CLR(vis,0);
for(int i=1;i<n;i++)
{
scanf("%d%d%d",&u,&v,&w);
u++,v++; //node.next[]:从1开始保存编号,编号为0表示不存在结点
addedge(u,v,w);
}
node[0].init();
dfs(1,0); //0这个数,编号加1变1了
int ans=0; //val数组
for(int i=1;i<=n;i++)
{
ans=max(ans,query(val[i]));
add(val[i]);
}
printf("%d\n",ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: