P7470 [NOI Online 2021 提高组] 岛屿探险

发布时间 2023-11-29 11:42:01作者: 蒟蒻·廖子阳

我永远喜欢数据结构。

题目传送门

  • 给出 \(n\) 个二元组 \((a_i,b_i)\),有 \(q\) 次询问,每次给出 \(l_i,r_i,c_i,d_i\),求有多少个 \(j\) 满足 \(j\in[l_i,r_i]\)\(a_j\oplus c_i\le \min\{b_j,d_i\}\)

  • \(n,q\le 10^5\),设值域为 \(V\)\(|V|\le 2^{24}\)

  • \(2.00\,\text{s}\,/\,256.00\,\text{MB}\)

一句话题解:离线,拆 \(\min\),考虑贡献。

我们知道线段树可以将一个区间拆成 \(\mathcal{O}(\log n)\) 个不交的节点,使得这些节点的并等于给定的区间。

于是我们考虑离线,在线段树上将每个询问拆成 \(\mathcal{O}(\log n)\) 个子区间,分别求解这些子区间的答案。

考虑对于线段树的一个节点 \([L,R]\),求解挂在它上面的询问。将这些询问按照 \(d_i\) 从小到大排序,同时将节点内的二元组按照 \(b_j\) 从小到大排序。

对于一个询问,分成 \(b_j<d_i\)\(b_j\ge d_i\) 两类二元组。不难发现排序后存在一个 \(\boldsymbol k\) 使得 \(\boldsymbol{k\in[L,R]}\) 且当 \(\boldsymbol{j\in[L,k)}\) 时,\(\boldsymbol{b_j<d_i}\);当 \(\boldsymbol{j\in[k,R]}\) 时,\(\boldsymbol{b_j\ge d_i}\)

即,两类二元组分别在一个连续区间内。

  • 对于 \(b_j\ge d_i\) 的部分,\(\min\{b_j,d_i\}=d_i\)

    这部分较为平凡。就是求区间内有多少个数异或定值后不超过定值。使用 01 trie 维护,将区间内所有数加入 01 trie,统计根节点(即全局)的答案。

    考虑一个子问题:统计以 \(u\) 为根的子树内的答案。维护每个点子树内部的答案。讨论 \(d_i\) 当前位是 \(0\) 还是 \(1\) 即可。若是 \(1\),则另这一位取 \(0\) 的数都满足条件需要被统计。这样的数在当 \(u\) 的某个儿子内。维护一个 \(sz_u\) 表示以 \(u\) 为根的子树内,有多少个询问内的数。用 \(sz_u\) 以及递归儿子求解即可。

  • 对于 \(b_j<d_i\) 的部分,\(\min\{b_j,d_i\}=b_j\)

    对于区间内的每个二元组,将其插入 01 trie,并考虑它会对哪些询问的 \(c_i\) 产生贡献。此时的 01 trie 是对于 \(c_i\) 维护的。

    同样考虑这个二元组会被以 \(u\) 为根的子树内的哪些数统计。讨论 \(b_j\) 当前位是 \(0\) 还是 \(1\)。如果是 \(1\),意味着另这一位为 \(0\)\(c_i\) 都会统计到这个二元组。这样的 \(c_i\) 也在某个儿子的 \(u\) 的子树内。即子树内所有数的贡献 \(+1\)。此时 \(sz_u\) 数组变成懒标记,使得某个叶子的初始答案,加上它即其祖先的懒标记后,等于其在当前加入的二元组构成的集合中的答案。

    对于一个询问,查询 \(c_i\) 的答案即可。由于初始空集合答案为 \(0\),只需要统计根链上所有点的标记之和。

    本质上是动态开点线段树。

由于实现的原因,01 trie 需要支持删除操作。因此我们不能简单地把空节点理解成不存在的节点。此处 01 trie 上的空节点定义为,仅当 \(\boldsymbol{sz_u=0}\) 时,\(\boldsymbol{u}\) 为空。注意那个是必要条件。这样我们不去计算空节点的原因不是因为其不存在,而是因为其没有贡献

同样要再次强调这句话,本质上是动态开点线段树

我们发现,由于排过序,若按顺序处理询问,\(\boldsymbol{k}\) 单调不降

每次 \(k\) 移动时,将当前位置从第一类的 01 trie 中删除,加入第二类的 01 trie。这样在一个线段树节点内,一个二元组被操作的次数是 \(\mathcal{O}(1)\) 的。单次操作时间复杂度为 \(\mathcal{O}(\log |V|)\),一个二元组会在 \(\mathcal{O}(\log n)\) 个线段树节点中被操作。

对于一个询问,拆分成了 \(\mathcal{O}(\log n)\) 段,一段查询的时间复杂度为 \(\mathcal{O}(\log |V|)\)

所以,时间复杂度为 \(\mathcal{O}((n+q)\log n\log |V|)\),空间复杂度为 \(\mathcal{O}((n+q)\log |V|)\)

提交记录(\(\color{white}\colorbox{limegreen}{Accepted}\) \(\color{limegreen}\bf 100\)\(\bf 8.00\,s\,/\,54.18\,MB\) 代码