为不同的x

时间:2016-01-07 15:40:56

标签: algorithm math

想象一个大小 n 的数组 A ,其中每个数组元素包含两个正整数 a i 和的 b'子> I

有Q查询,其中每个查询可以是以下两种类型之一:

1)给定正整数 x ,找到 max(a i x + b i 所有 1 n

2)为某些 i a i b i 的值>

查询的数量可能很大,因此天真O(Q * n)算法不够。此外,x可以大到10 9 ,目标表达式的值可以大到10 18

可以使用某些段树的变体来解决这个问题吗?如果有类似的问题,请指出我。另外,你会如何解决这个问题呢?我不是在寻找代码,只是寻找逻辑的一些提示/指针。

修改:您可以假设查询类型1中的 x 值不会减少。

编辑2 :您可以假设在更新中, a 的值只会增加。

编辑3 :我找到了问题here的答案。感谢@Mikhail指出信封这个词。使用这个词太多次都有帮助; - )

2 个答案:

答案 0 :(得分:3)

首先订购x值。 根据x0(最低x)处的值对线(ax + b是线)的值进行排序。 对于每对连续的线,找出它们的交叉点(更准确地说是交叉点的x坐标)。如果x坐标低于x0,则可以忽略该对。对于其他对,保留优先级队列以找到下一个交叉点。

现在,对于下一个交叉点之前的所有输入x,您输出当前的前导线。由于没有交叉点,订单不会改变,所以它们都是相同的线。到达交叉点时,意味着您的两条线交叉。因此,在排序列表中交换它们,并再次添加与新邻居的交叉点。

重复直到解决了所有输入案例。 如果交点不足,则意味着不会有任何其他订单更改,因此输出所有其他输入的最大值。

复杂度很可能是初始排序的结果,如果行数相对较小或者你必须做的更新次数是N ^ 2/2(每行与每一行相交),O(QlogQ + N ^ 2 * logN)

答案 1 :(得分:3)

@ Sorin的想法是正确的,但找到交叉点是低效的。你想要的是构建所有行的上包络。此信封将包含不超过N-1个点(每个行在信封中的贡献不会超过一次)。因此,扫描其已排序的点将花费O(N+Q)时间。

enter image description here

现在,我们将在O(NlogN)时间内构建信封。首先,使用升序a对所有行进行排序。这将需要O(NlogN)。让我们假设为了简单起见a的所有值都不同,这不会改变主要思想。

请注意,行0N-1将形成信封的左右斜率。因为接近无穷大,常数b不会发生变化。让我们从行0开始,作为信封的第一部分。现在,我们将按升序a的顺序浏览所有其他行,更新每一步的包络。我还要多次重复“信封”?嗯...

信封的更新(该死的!)与Graham scan的工作方式非常相似。在第一步,我们将行0放在堆栈中。在每一步中,我们考虑下一行并丢弃堆栈顶部的所有行,直到新行适合。见图:

enter image description here

这里我们添加行i+1。为此,我们需要丢弃行ii-1。由于每行都添加到此堆栈并从中删除不超过一次,因此整个扫描仅需O(N)。如您所见,排序一直都在进行。

想知道如何确定下一行是否适合当前的堆栈?比较i+1行与i之上的行i的交叉点以及i-1上的O(N)

现在我们有了信封(我发誓,这是最后一次),其余的很容易。我们有解决方案在O(Q + NlogN)工作。 HTH。

顺便提一下,有人指出这个算法很受欢迎,被称为Convex hull trick

UPD 哇,我忘记了我们还需要不时更新信封:)这使问题复杂化。需要考虑一下。

无论如何,与处理O(1)中类型1和O(1)中类型2的查询的天真方法相比,这提供了相反的结果:类型1的O(N),{类型2 {1}}。