P7470 岛屿探险题解

很神奇的题目。

首先你考虑我们如何把那个异或转化掉。直接异或显然是不好做的。

异或考虑拆位。考虑我们如何比较两个二进制数的大小?我们从高到低考虑,如果一个是 而另一个是 那么前一个就小于后一个了。

我们忽略 的限制,将所有的 都插入到一颗 trie 树上。考虑此时插入 。我们用 表示 二进制从高到低的第 位,第 位是代表 的那位。假设插入到第 位,假设 ,那么我们反转 ,那么之后 之后的位无论怎么填都会小于 了。这就对应了 trie 树上的一颗子树。

因此,我们成功将一个异或的限制转化成了 个子树,可以用 dfn 编号的方法转化成不相交的 个区间。然后我们发现对于每个岛屿也可以用类似的方法转化成 个区间,表示这个岛屿对询问的 的限制。因此实际上我们是要对这个进行计数: 其中 分别表示转化之后的若干个区间。

我们发现同一个询问或岛屿的所有区间是不交的,因此我们直接拆成 次操作也不会算重。对 那一维进行扫描线,我们发现这是一个动态二位数点问题。直接上 CDQ 分治!复杂度 。似乎有点爆炸。

你发现第三个限制似乎不是很美观。实际上,我们只需要考虑 中较小的那个即可。因此我们直接分 两种情况,我们对 这个维度扫描线,对另外两个维度进行二位数点即可。但是这个复杂度没有更优啊?

我们不妨换一个角度。考虑树套树,我们直接在 tire 树上每个结点开一颗线段树,插入岛屿的时候就直接给经过节点的对应位置加一即可,查询就是区间查询。

此时你发现这个算法复杂度就变成 了。

但是树套树太难写了怎么办?我们发现,有 次查询,每次查询是 的。这里造成了复杂度的瓶颈。实际上,trie 树相当于一颗值域线段树,而我们每次要查询一个节点的和,所以我们其实可以 直接查询。换个角度想,其实就是暴力维护树上前缀和,但是树高只有 。在 CDQ 分治的时候,插入时对对应节点的 加一,查询的时候累计上对应节点的和即可。由于插入的节点显然和询问次数有关,所以直接暴力删掉 trie 的所有节点复杂度也没有问题。这实际上对应了前面问题的静态版本,我们通过 CDQ 花费一个 的复杂度将问题转化成了一个静态问题。

参考代码在这里


P7470 岛屿探险题解
https://blogs.sving1024.top/posts/49226/
发布于
2026年1月9日
许可协议