01 Trie

构造

01 Trie 一般是从高位向低位建树,因为高位的贡献大,方便贪心。

void add(int x) {
	int p = 0;
	for (int i = 30; i >= 0; i--) { //int
		int d = (x >> i) & 1;
		if (!trie[p][d]) trie[p][d] = ++cnt;
		p = trie[p][d];
	}
	val[p] = x;
}

查询

01 Trie 的查询一般是这样的形式:给定一个数 x x x,求与 x x x 异或最大的数是多少。假设已经建立了所有数的 01 Trie,那么找这个数的复杂度近似 O ( 1 ) O(1) O(1)。因为异或的特征是相同为 0 0 0,不同为 1 1 1,所以从高位到低位,贪心地走不一样的路。

int query(int x) {
	int p = 0;
	for (int i = 30; i >= 0; i--) {
		int d = (x >> i) & 1;
		if (trie[p][d ^ 1]) p = trie[p][d ^ 1];
		else if (trie[p][d]) p = trie[p][d]; // ( ) 不要落了
	}
	return val[p];
}

删除

如何删除 Trie 树上的一个数字?只需要记录一个 v i s vis vis,为经过某个点的数字的数量,删的时候沿途 v i s −  ⁣ − vis-\!- vis。在查询的时候,走 v i s > 0 vis > 0 vis>0 的点即可。

void update(int x, int k) {
	int p = 0; 
	for (int i = 20; i >= 0; i--) {
		int d = (x >> i) & 1;
		p = trie[p][d]; vis[p] += k;
	}
}

Trie 树合并

给定一个数 x x x,求与 x x x and、or 最大的数是多少。

那么还是从高到低建 01 Trie。以 and 为栗子,从高到低贪心时,如果当前位是 1 1 1,那么在树上就走 1 1 1。如果当前位是 0 0 0,那么 1 1 1 0 0 0 都可以,怎么办?

可以在建出字典树后,用类似线段树合并的方法,合并 Trie 树每个点的左右儿子,即为把 0 0 0 变成 01 01 01 的结合体。注意合并的顺序,先合并儿子。这样查询的时候,就放心往 0 0 0 走就行了。

void merge(int &x, int y) {
	if (!x || !y) {
		x += y;
		return ;
	}
	merge(trie[x][0], trie[y][0]);
	merge(trie[x][1], trie[y][1]);
}
void dfs(int x) {
	if (trie[x][0]) dfs(trie[x][0]);
	if (trie[x][1]) dfs(trie[x][1]);
	merge(trie[x][0], trie[x][1]);
}
int query_and(int x) { 
	int p = 0, ans = 0;
	for (int i = 20; i >= 0; i--) {
		int d = (x >> i) & 1;
		if (d == 1 && trie[p][1] && vis[trie[p][1]] > 0) p = trie[p][1]; 
		else if (trie[p][0]&& vis[trie[p][0]] > 0) p = trie[p][0];
	}
	return val[p];
}

Trick

a ⊕ b ⊕ b = a a \oplus b \oplus b= a abb=a

∀ x = ⊕ [ 1 , l ] , y = ⊕ [ 1 , r ] → ⊕ l , r = x ⊕ y \forall x = \oplus_{[1,l]}, y = \oplus_{[1,r]} \to\oplus_{l,r} = x \oplus y x=[1,l],y=[1,r]l,r=xy

∀ x = ⊕ ( a , b ) , y = ⊕ ( b , c ) → ⊕ ( a , c ) = x ⊕ y \forall x = \oplus_{(a,b)}, y = \oplus_{(b,c)} \to \oplus_{(a,c)} = x \oplus y x=(a,b),y=(b,c)(a,c)=xy

P4551 最长异或路径

⊕ ( u , v ) \oplus_{(u,v)} (u,v) 转化成 ⊕ u \oplus_{u} u ⊕ v \oplus_{v} v。建立一棵字典树,枚举一个 ⊕ u \oplus_{u} u ,在字典树上贪心去找最大的 ⊕ v \oplus_{v} v,并更新答案即可。

ZR956 集合


相关内容 AT3913 XOR Tree

这一道题和 01 Trie 的关系不大,但关于异或的知识有许多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值