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;
}
}
flag:如果用了 Trie 树合并,那么删除的复杂度可能会被卡到 O ( n ) O(n) O(n)。
给定一个数 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];
}
a ⊕ b ⊕ b = a a \oplus b \oplus b= a a⊕b⊕b=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=x⊕y
∀ 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)=x⊕y
将 ⊕ ( u , v ) \oplus_{(u,v)} ⊕(u,v) 转化成 ⊕ u \oplus_{u} ⊕u 和 ⊕ v \oplus_{v} ⊕v。建立一棵字典树,枚举一个 ⊕ u \oplus_{u} ⊕u ,在字典树上贪心去找最大的 ⊕ v \oplus_{v} ⊕v,并更新答案即可。
相关内容 AT3913 XOR Tree
这一道题和 01 Trie 的关系不大,但关于异或的知识有许多。