博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
字典树--Xor问题
阅读量:5970 次
发布时间:2019-06-19

本文共 3742 字,大约阅读时间需要 12 分钟。

字典树大家都知道吧,如果不知道可以看这里,我的模板写得还是不错的:


接下来我们先看一个问题,通过这个问题来了解Xor这个运算的基本性质:

题目大意:给定一棵带权树,多次询问两点之间边权异或和。
性质:a^x^x=a,其中^代表Xor运算,证明略。
利用这条性质,我们可以任取一点为根,通过一次dfs算出每个点到根节点的异或和val[u],假设每次询问的点是u,v,则答案就是val[u]^val[v]。
过程:val(u,v)
=val(u,lca)Xor val(v,lca)
=val(u,root)Xor val(lca,root)Xor val(v,root)Xor val(lca,root)
=val(u,root)Xor val(v,root)
此题代码:

//Âå¹È2420 ÈÃÎÒÃÇÒì»ò°É #include
#include
#define ll long longusing namespace std;inline int read(){ int x=0;char ch=' '; while(ch<'0'||ch>'9')ch=getchar(); while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar(); return x;}struct edge{ int to,next,w;}e[200001];int n,m,tot;int head[100001];int v[100001];int st[100001][20];int dep[100001];void dfs(int x,int fa){ st[x][0]=fa; for(int i=head[x];i;i=e[i].next){ int u=e[i].to; if(u==fa)continue; v[u]=v[x]^e[i].w; dep[u]=dep[x]+1; dfs(u,x); }}inline void addedge(int x,int y,int l){ e[++tot].to=y;e[tot].next=head[x];e[tot].w=l;head[x]=tot;}int main(){ n=read(); for(int i=1;i<=n-1;i++){ int x=read(),y=read(),l=read(); addedge(x,y,l);addedge(y,x,l); } dfs(1,0); m=read(); for(int i=1;i<=m;i++){ int x=read(),y=read(); int ans=v[x]^v[y]; printf("%d\n",ans); } return 0;}

接下来就是正题了——如何用字典树来解决Xor问题?

题目链接:
整体思路:Trie树+贪心
我们要选出一个数来使它和另一个数的异或和最大,那么当然要转二进制来看了。
转了二进制之后我们可以发现高位的值比所有低位的值相加还要大,那么就有贪心思路了:
建立一棵01Trie(每个点只有0和1两个儿子),然后将每个初始数字从高位到低位按位插入进来,然后每次查询时每一步尽量走和它那一位不同的方向,最后输出即可。
此题代码:

//hdu4825 Xor Sum#include
#include
#define ll long longusing namespace std;inline int read(){ int x=0;char ch=' '; while(ch<'0'||ch>'9')ch=getchar(); while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar(); return x;}int n,m,tot;int ch[3100001][2];int val[3100001];void insert(int x){ int u=0; for(int i=31;i>=0;i--){ int c=(x>>i)&1; if(!ch[u][c]){ ch[u][c]=++tot; } u=ch[u][c]; } val[u]=x;}int find(int x){ int u=0; for(int i=31;i>=0;i--){ int c=(x>>i)&1; if(ch[u][c^1]){ u=ch[u][c^1]; } else{ u=ch[u][c]; } } return val[u];}int main(){ int T=read(); int k=1; while(T--){ printf("Case #%d:\n",k++); memset(ch,0,sizeof(ch)); memset(val,0,sizeof(val)); tot=0; n=read();m=read(); for(int i=1;i<=n;i++){ insert(read()); } for(int i=1;i<=m;i++){ printf("%d\n",find(read())); } } return 0;}

接下来我们将第一个问题和第二个问题结合在一起来看一个题;

题目链接:
题目大意:给定一棵树,求最大异或路径。
思路:我们可以每次固定一个点,寻找和它异或路径最大的点,更新答案。这时问题就转化成了在树上已知一个点如何找另一个点和它异或路径最大。
结合T1和T2,那么我们可以表示出两点间的异或路径:val(u)^val(v),其中因为我们已经固定了一个点了,那么就是要找一个值和已知值异或值最大了,这就是T2的问题。
方案就很明显了,dfs求出每个点到根节点的异或值,然后将这些值插入到一棵01Trie中,枚举每个点找从这个点出发的最大异或路径,并更新答案。
此题代码:

//poj3764 The xor-longest Path#include
#include
#include
#define ll long longusing namespace std;int v[100001];int ch[3100001][2];int n;struct edge{ int to,next,w;}e[200001];int head[100001];void dfs(int x,int fa){ for(int i=head[x];i;i=e[i].next){ int u=e[i].to; if(u==fa)continue; v[u]=v[x]^e[i].w; dfs(u,x); }}int tot;inline void addedge(int x,int y,int l){ e[++tot].to=y; e[tot].next=head[x]; head[x]=tot; e[tot].w=l;}void insert(int x){ int u=0; for(int i=30;i>=0;i--){ int c=(x>>i)&1; if(!ch[u][c]){ ch[u][c]=++tot; } u=ch[u][c]; }}int find(int x){ int u=0;int ans=0; for(int i=30;i>=0;i--){ int c=(x>>i)&1; if(ch[u][c^1]){ ans+=(1<

转载于:https://www.cnblogs.com/stone41123/p/7581243.html

你可能感兴趣的文章
数值积分中的辛普森方法及其误差估计
查看>>
Web service (一) 原理和项目开发实战
查看>>
跑带宽度多少合适_跑步机选购跑带要多宽,你的身体早就告诉你了
查看>>
深入理解Java的接口和抽象类
查看>>
Javascript异步数据的同步处理方法
查看>>
iis6 zencart1.39 伪静态规则
查看>>
SQL Server代理(3/12):代理警报和操作员
查看>>
Linux备份ifcfg-eth0文件导致的网络故障问题
查看>>
2018年尾总结——稳中成长
查看>>
通过jsp请求Servlet来操作HBASE
查看>>
Shell编程基础
查看>>
Shell之Sed常用用法
查看>>
Centos下基于Hadoop安装Spark(分布式)
查看>>
mysql开启binlog
查看>>
设置Eclipse编码方式
查看>>
分布式系统唯一ID生成方案汇总【转】
查看>>
并查集hdu1232
查看>>
Mysql 监视工具
查看>>
Linux Namespace系列(09):利用Namespace创建一个简单可用的容器
查看>>
博客搬家了
查看>>