Algorithm 半群运算符(并集)的范围查询

Algorithm 半群运算符(并集)的范围查询,algorithm,Algorithm,我希望实现一个算法,该算法给定一个整数数组和数组中的范围(间隔)列表,返回每个间隔中不同元素的数量。也就是说,给定数组A和范围[i,j],返回集合{A[i],A[i+1],…,A[j]}的大小 显然,天真的方法(从i迭代到j,计数忽略重复项)太慢了。范围和似乎不适用,因为U B-B并不总是等于B 我在Wikipedia中查找了范围查询,它暗示Yao(82年)展示了一种算法,该算法对半群运算符(union似乎是)执行此操作,具有线性预处理时间和空间以及几乎恒定的查询时间。不幸的是,这篇文章不能免费

我希望实现一个算法,该算法给定一个整数数组和数组中的范围(间隔)列表,返回每个间隔中不同元素的数量。也就是说,给定数组A和范围[i,j],返回集合{A[i],A[i+1],…,A[j]}的大小

显然,天真的方法(从i迭代到j,计数忽略重复项)太慢了。范围和似乎不适用,因为U B-B并不总是等于B

我在Wikipedia中查找了范围查询,它暗示Yao(82年)展示了一种算法,该算法对半群运算符(union似乎是)执行此操作,具有线性预处理时间和空间以及几乎恒定的查询时间。不幸的是,这篇文章不能免费提供


编辑:这个问题似乎可以在

中找到。有一个相当简单的算法,它使用O(logn)时间和空间进行预处理,每个查询使用O(logn)时间。首先,创建一个持久段树来回答范围和查询(最初,它应该在所有位置包含零)。然后遍历给定数组的所有元素,并存储每个数字的最新位置。在每次迭代中,创建一个新版本的持久段树,将1置于每个元素的最新位置(在每次迭代中,只能更新一个元素的位置,因此段树中只有一个位置的值会发生变化,因此可以在O(logn)中进行更新)。要回答一个查询(l,r),您只需要在(l,r)段上找到树版本的sum,该树是在遍历初始数组的r元素时创建的。 希望这个算法足够快。
Upd。在我的解释中有一个小错误:在每一步中,段树中最多有两个位置的值可能会改变(因为有必要将0放在一个数字的前一个最新位置,如果它被更新的话)。但是,它不会改变复杂性。

您可以通过执行二次时间预计算来回答恒定时间内的任何查询:

For every i from 0 to n-1
    S <- new empty set backed by hashtable;
    C <- 0;
    For every j from i to n-1
        If A[j] does not belong to S, increment C and add A[j] to S.
        Stock C as the answer for the query associated to interval i..j.
对于从0到n-1的每个i
S=i
。这将保证摊销后的复杂度将保持二次型,并且您不会被迫在开始时执行完整的二次型预计算


请注意,显而易见的算法(您在语句中称之为显而易见的算法)是立方的,因为您可以完全扫描每个间隔。

这里是另一种可能与段树密切相关的方法。将数组的元素视为完整二叉树的叶子。如果数组中有2^n个元素,则该完整树有n个级别。在树的每个内部节点上,存储位于树下叶子上的点的并集。数组中的每个数字需要在每个级别显示一次(如果有重复项,则显示次数会减少)。因此,空间成本是logn的一个因素

考虑一个长度为K的范围a..B。只要这些节点下的子树完全包含在该范围内,就可以通过形成与叶子和节点相关联的集合的并集来计算该范围内点的并集,并尽可能在树的高处拾取节点。如果沿着范围选择尽可能大的子树,您会发现子树的大小先增大后减小,所需子树的数量仅随范围大小的对数而增长-开始时,如果您只能选择大小为2^k的子树,它将在可被2整除的边界上结束^(k+1)如果你的范围足够大,下一步你将有机会得到至少2^(k+1)大小的子树


因此,回答一个查询所需的半群操作数为O(logn)-但请注意,半群操作可能很昂贵,因为您可能正在形成两个大集合的并集。

我们不能将段树复制n次,因为这需要n^2个时间(和空间)。也许可以在迭代r时更新相同的段树,然后回答所有的查询(l,r)。需要再考虑一下。我们不需要复制树,因为它不是普通的段树。它是一个持久段树,可以更改O(logn)中的一个元素时间和空间。你能告诉我什么是持久段树吗?我知道的版本也可以更改O(logN)中的元素,但一旦更改,它的旧版本就消失了,你不能再查询它了。找到这个()这正是我在搜索持久段树的描述时遇到的问题。我不确定谷歌为什么不早点给我指出这一点。经过深思熟虑,我认为我可以用我熟悉的段树版本来实现这一点,不过如果你给我指一个持久段树的描述,我还是会很感激的。显而易见的alg算法是针对每个查询[i,j],从i迭代到j,并计算A中不同元素的数量。由于每个查询最多有N次迭代,这种算法的复杂度为O(N*Q),其中Q是查询的数量。你是对的,它在N中可能是立方的,正如在N^2个可能的查询的顺序上一样。没有人说确实存在N^2个查询……不,你没有提供关于不同查询数量的额外信息,这就是为什么我给出了另一种动态预计算算法。它也有O(N*Q)复杂度,但它比明显的算法具有更好的摊销复杂度。它更适合于您不希望执行重要部分查询的情况。无论哪种方式,我都需要一个比N*Q更快的解决方案。好的,但您需要告诉我您希望在什么意义上实现这一点:最坏情况、平均情况、摊销复杂度或其他你给出的概念。这很重要,因为我们在时间/空间/精确性权衡不可避免的极限下工作。欢迎来到堆栈溢出!!既然你对实现算法感兴趣,请告诉我们一些关于你的数据和