如何在更新操作比简单的加法或乘法更为复杂时,应用惰性更新方法来更新线段树?

3
考虑问题。在这个段树解决方案中,我们正在更新给定范围内树的所有节点。是否可以将延迟传播应用于此问题?
编辑:请注意,在每次更新操作中arr[i]=(1-(1-arr[i])*a),其中L<=i<=Ra是一个常数。
2个回答

3

我会假设你的查询操作是在范围[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

更新懒惰字段时,明显只需增加 a 的指数。

您可以像链接的懒惰传播文章中描述的那样懒惰地执行这 3 个操作。

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


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*a1-b*a
问题在于系数可能增长得太快,很快就会超出存储类型(intdouble或其他)的容量。如果该问题涉及模某个整数的整数余数而不仅仅是整数或实数,则这不是问题;否则,存储系数很快就会成为问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接