01trie树学习笔记
问题引入
给一个长为 n n n的数列,要求一个 a i a_i ai, a j a_j aj,使得 a i x o r a j a_i xor a_j aixoraj最大。
什么是01trie树
类似于字典树的做法,将每一个数化为二进制数,看作01串,插入到字典树中。
问题解答
首先建好01trie树,然后对于每一个数,在树上跑一遍贪心,即贪心选择与这个数当前位不同的那个节点。
也就是说,尽可能地保证越高的位的异或和为1,(因为高位的一个1比后面的位全为1都要大)
比如对于7,在下面这个插入了0,2,7的树上跑。
开始在0号节点,然后看最高位,7的最高位是1,所以为了保证当前位异或和为1,应走0的方向,也就是走到1节点。
然后看次高位,7的次高位为1,同理,走0的方向到3号节点,依次类推,得出与7异或的最大值为7。
然后遍历整个数组,对每一个数都这么跑一遍,就得出了异或最大值7.
复杂度
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn).
两个扩展例题
1.第 k k k大异或和
题意:还是给出长为
n
n
n的序列,这次不求最大值了,求第
k
k
k大的
a
i
x
o
r
a
j
a_i xor a_j
aixoraj
做法:考虑二分答案,对于每个二分到的值
x
x
x,遍历数组跑一遍01trie树,看有多少组异或和是大于
x
x
x的,然后根据大于
x
x
x的异或和的组数向上向下二分。
如何计算大于
x
x
x的组数?
遍历数组,对于每一个数
a
i
a_i
ai,跑一遍01trie,记录比
x
x
x大的异或和的组数
a
n
s
ans
ans。对于
x
x
x的每一位,若为1,则走树上与
a
i
a_i
ai当前位相反的儿子,若为0,则走树上与
a
i
a_i
ai当前位相同的儿子,并将与
a
i
a_i
ai当前位相反的那个儿子的子树大小加到
a
n
s
ans
ans中。
由于每一组大于
x
x
x的二元组都会被计算两次,所以最终的
a
n
s
ans
ans还要除以二。
复杂度
O
(
n
l
o
g
n
l
o
g
n
)
O(nlognlogn)
O(nlognlogn).
2.树上最大异或路径
题意:给定一棵
n
n
n个点的带权树,寻找树中找两个结点,求最长的异或路径。
异或路径指的是指两个结点之间唯一路径上的所有边权的异或。
做法:观察得到,从
i
i
i~
j
j
j的路径的异或值为
i
i
i到根的异或和
x
o
r
xor
xor
j
j
j到根的异或和。
因此将例题中的
a
i
a_i
ai转化成节点
i
i
i到根的异或和的值,就可以照着例题的方法做了。
复杂度
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn).