01字典树模板详解

一、字典树

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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值