POJ 3764 The xor-longest Path (01字典树)
<题目链接>
题目大意:
给定一颗$n$个节点$(n\leq10^5)$,有边权的树,其边权$(0\leq w < 2^{31})$。让你求出这棵树上任意两个节点之间的异或最大值。
解题分析:
先用DFS预处理出根节点到所有节点的路径异或值,然后任意两点$u,v$之间的路径异或值就能通过 $(u->rt) xor (v->rt)$的异或值得到,因为根节点到u、v的最近公共祖先的路径被异或了两次,所以能够直接得到u,v之间的异或距离。但是因为节点数量有$10^5$个,直接枚举两两节点肯定超时。用01字典树优化一下,快速找到异或的最大值。
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> using namespace std; typedef long long ll; template<typename T> inline void read(T&x){ x=0;int f=1;char ch=getchar(); while(ch<'0' ||ch>'9'){ if(ch=='-')f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); } x*=f; } #define rep(i,s,t) for(int i=s;i<=t;i++) #define clr(a,b) memset(a,b,sizeof(a)) const int N = 1e5+5; ll nxt[N*32][2],xoval[N]; int n,rt; struct Edge{int to,nxt;ll w; }e[N<<1]; int head[N],cnt,pos; inline void init(){ cnt=0;clr(head,-1); pos=1;clr(nxt,0);clr(xoval,0); } inline void add(int u,int v,int w){ e[cnt]=(Edge){v,head[u],w};head[u]=cnt++; } void dfs(int u,int pre,ll nowval){ //得到根到所有点的异或值 xoval[u]=nowval; for(int i=head[u];~i;i=e[i].nxt){ int v=e[i].to; if(v==pre)continue; dfs(v,u,nowval^e[i].w); } } inline void Insert(ll x){ int now=1; for(int i=30;i>=0;i--){ int to=(x>>i)&1; if(!nxt[now][to])nxt[now][to]=++pos; now=nxt[now][to]; //num[now]++; } } /*inline void del(ll x){ //将01字典树中删除x int now=1; for(int i=30;i>=0;i--){ int to=(x>>i)&1; num[now]--; now=nxt[now][to]; } }*/ inline ll query(ll x){ int now=1;ll ans=0; for(int i=30;i>=0;i--){ int to=(x>>i)&1; if(nxt[now][to^1])now=nxt[now][to^1],ans+=(1<<i); else now=nxt[now][to]; } return ans; } int main(){ while(~scanf("%d",&n)){ init(); rep(i,1,n-1){ int u,v;ll w;read(u);read(v);read(w); add(u,v,w);add(v,u,w); } dfs(0,-1,0); ll ans=-1e18; rep(i,0,n-1){ ll res=query(xoval[i]); ans=max(ans,res); Insert(xoval[i]); } printf("%lld\n",ans); } }
作者:is_ok
出处:http://www.cnblogs.com/00isok/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。