如果更新比简单的加法或乘法更复杂,如何应用惰性方法来更新段树?

时间:2018-04-13 20:04:35

标签: algorithm data-structures segment-tree range-query lazy-propagation

考虑this问题。在此分段树解决方案中,我们将更新给定范围内树的所有节点。是否可以将lazy propagation应用于此问题?

修改:请考虑在每个更新操作arr[i] = (1-(1-arr[i])*a)中,其中L<=i<=Ra是常量。

2 个答案:

答案 0 :(得分:2)

是的,至少在某些情况下确实是可能的。

基本上,您需要一种有效存储延迟操作的方法,以及一种有效地将两个存储的延迟操作合并为一个的方法。

例如,假设更新操作是段分配,即a[l] = xa[l+1] = x...a[r-1] = xa[r] = x。 整个子树上的此操作可以仅存储为值x,这意味着操作是将x分配给此子树的每个顶点。 对于顶点v中的延迟传播,我们只将它应用于顶点v的直接子节点,并在那里存储相同的延迟操作x。 请注意,任务中的任何旧的延迟操作都会被分配删除。 这就是作业的本质。

至于您添加的示例操作arr[i] = (1 - (1 - arr[i]) * a),让我们看看在使用常量ab进行两次此类操作后值的变化。

在操作之前,请将值设为v

在第一个之后,它变为w = 1 - (1 - v) * a,即a*v + (1-a)*1

在第二次操作之后,该值变为1 - (1 - w) * b,即b*w + (1-b)*1,而b*a*v + b*(1-a)*1 + (1-b)*1又为(b*a)*v + (1-b*a)*1,最后变为b*a。 (我可能混淆了+ s和-s,但希望不会改变整体情况。)

我们现在可以看到该值是原始值的线性函数,因此我们可以分别存储线性和常数项的系数1-b*aint

现在的问题是系数可能会增长得太快,并且很快就会超过存储类型的容量(double,{{1}}或其他什么)。 现在,如果问题处理整数残差模数某些整数而不仅仅是整数或实数,那不是问题;否则,存储系数很快就会出现问题。

答案 1 :(得分:2)

我假设您的查询操作是在[L, R]范围内找到总和。

您当然希望有效地执行此操作,可能每次操作O(log n)

您需要一种在lazy字段中存储数据的方法,该字段允许您在遍历查询树时计算更新。

让我们看看我们是否可以更好地编写更新:

v = 1 - (1 - v) * a
  = 1 - (a - av)
  = 1 - a + av

如果我们两次这样做:

1 - a + av = 1 - (1 - [1 - a + av]) * a
           =  1 - (a - a + a**2 - a**2 v)
           = 1 - a + a - a**2 + a**2 v
           = 1 - a**2 + a**2 v

相当于(适用于整个范围):

  1. 乘以a
  2. 减去a
  3. 添加1
  4. 更新惰性字段时,很明显只需增加a的指数。

    您可以按照链接到的延迟传播文章中的说明懒惰地执行所有这些操作。

    因此,您的更新操作可以分为3个延迟更新,每个更新都在O(log n)时间内完成,总时间为O(log n)