一、字典树
1.01字典树的常见问法
给定n个整数a1,...,an,任选两个数进行异或运算,得到的最大结果是多少?
1<=N<=10^5,0<=a[i]<=2^31
2.思路
1.异或运算->二进制位->n个整数转化为二进制表示->二进制是01构成的串->构造tire树->在树上进行异或运算
2.^不同为1,相同为0 ,尽量走相反位,异或值最大
3.有关trie
1.trie存单词(26个字母),是一颗26叉树,深度为最长单词的长度
2.trie存整数(十进制),是一颗10叉树,深度为10层
3.trie存整数(二进制),是一颗二叉树,深度为31层
4.时间复杂度
n个数,m个询问 ,将ans[i]加入01字典树 ,o(n*log(ans))
二、模板
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
using namespace std;
const int inf=0x3f3f3f3f;
const int N=100010;
int n,a[N];
char ch[N*31][2],idx;//31层,两条边(0|1)
void insert(int x)//插入整数的函数
{
int p=0;
for(int i=31; i>=0; i--)
{
int j=x>>i&1;//取出第i位
if(!ch[p][j])
ch[p][j]=++idx;
p=ch[p][j];
}
}
int query(int x)
{
int p=0,res=0;
for(int i=31; i>=0; i--)
{
int j=x>>i&1;//若j=0,ch[p][!j]=ch[p][1]
if(ch[p][!j])//若j=1,ch[p][!j]=ch[p][0]
{
res+=1<<i;//左移i位,相当于2^i, 累加位权(例如1<<2,1左移两位,100,4,)
p=ch[p][!j];
}
else
p=ch[p][j];
}
return res;
}
signed main()
{
cin>>n;
for(i=1; i<=n; i++)
{
cin>>a[i];
insert(a[i]);
}
int ans=0;
for(int i=1; i<=n; i++)
{
ans=max(ans,query(a[i]));
}
cout<<ans<<endl;
return 0;
}
三、简单介绍下>> 、<<
例如 :
(1) 2 >>x 删除右边x位,左边补0
(010|)>>30->(000)&(001)->0
(0|10)>>2->(000)&(001)->0
(01|0)>>1->(001)&(001)->1
(010)>>0->(010)&(001)->0
(010)^(101)->(111)->7
(011)^(101)->(110)->6
(111)^(010)->(101)->5
(2)
1<<i就相当于2^i;
三、例题
一、题目要求
题目描述
给定一棵 n 个点的带权树,结点下标从 11 开始到 n。寻找树中找两个结点,求最长的异或路径。
异或路径指的是指两个结点之间唯一路径上的所有边权的异或。
输入格式
第一行一个整数 n,表示点数。
接下来 n−1 行,给出 u,v,w ,分别表示树上的 u 点和 v 点有连边,边的权值是 w。
输出格式
一行,一个整数表示答案。
输入输出样例
输入
4 1 2 3 2 3 4 2 4 6
输出
7
说明/提示
最长异或序列是 1,2,3,答案是 7=3⊕4。
数据范围
1≤n≤100000;0<u,v≤n;0≤w<2^31。
二、思路
1.创建邻接链表
2.计算每个点到root(根)异或值(用dfs)
3.建立01字典树
4.查询
三、代码
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
using namespace std;
const int N=1e6+10;
const int inf=0x3f3f3f3f;
int n;
int he[N],tot;
int to[N*2],nex[N*2],w[N*2];
int dis[N];
int ch[N*32][2],idx;
void add(int u,int v,int z)//创建邻接表+加边
{
to[tot]=v;
w[tot]=z;
nex[tot]=he[u];
he[u]=tot++;
}
void dfs(int u,int father,int sum)
{
dis[u]=sum;
for(int i=he[u];~i;i=nex[i])
{
int j=to[i],ww=w[i];//ps:to[i]!!!
if(j!=father)//如果这条边的初边不是往上走的
{
dfs(j,u,sum^ww);
}
}
}
void insert(int x)
{
int p=0;
for(int i=31;i>=0;i--)
{
int j=x>>i&1;
if(!ch[p][j])
ch[p][j]=++idx;//不变
p=ch[p][j];
}
}
int query(int x)
{
int p=0,res=0;
for(int i=31;i>=0;i--)
{
int j=x>>i&1;
if(ch[p][!j])
{
res+=1<<i;
p=ch[p][!j];
}
else
p=ch[p][j];
}
return res;
}
void solve()
{
cin>>n;
int i,j;
memset(he,-1,sizeof(he));
for(int i=0;i<n-1;i++)//无向图
{
int u,v,w;
cin>>u>>v>>w;
add(u,v,w);
add(v,u,w);
}
dfs(0,-1,0);//挑0为根节点
/*for(i=0;i<n;i++)
{
cout<<dis[i]<<' ';
}
cout<<endl;*/
for(i=0;i<n;i++)
{
insert(dis[i]);
}
int ans=0;
for(i=0;i<n;i++)
{
ans=max(ans,query(dis[i]));
}
cout<<ans<<endl;
}
signed main()
{
int t=1;
while(t--)
{
solve();
}
return 0;
}