lazy-tag思想,记录每一个线段树节点的变化值,当这部分线段的一致性被破坏我们就将这个变化值传递给子区间,大大增加了线段树的效率。
在此通俗的解释我理解的Lazy意思:
现在需要对[a,b]区间值进行加c操作,那么就从根节点[1,n]开始调用update函数进行更新操作;如果刚好执行到一个rt节点,而且tree[rt].l == a && tree[rt].r == b,这时我们就应该一步更新此时rt节点的sum[rt]的值(sum[rt]+=c* (tree[rt].r - tree[rt].l + 1))。
关键来了,如果此时按照常规的线段树的update操作,这时候还应该更新rt子节点的sum[]值,而Lazy思想恰恰是暂时不更新rt子节点的sum[]值,而是在这里打一个tag,直接return。直到下次需要用到rt子节点的值的时候才去更新,这样避免许多可能无用的操作,从而节省时间 。
另外我们经常在树里面用到位运算,简单介绍一下:
(i<<n)==(i*2n) (i>>n)==(⌊i/2n⌋)
在找子树的时候,若父亲结点编号为i,则左右子结点分别表示为2i,2i+1,而树中就直接写为i<<1和i<<1|1(“|”详细自行百度),而寻找子节点可以表示为i>>1;
申请结构体的时候,要开到四倍长度空间,直接表示为i<<2;
这里再说明一下为什么要开四倍空间
假设我们用一个数组来头轻脚重地存储一个线段树,根节点是1,孩子节点分别是2n, 2n+1, 那么,设线段长为L(即[1..L+1))
设树的高度为H,对H,有:H(L)={1,1+H(⌈L2⌉)L>=1;
这是一个很简单的递归式,并用公式逐次代换,就等到 H(L)=k+H(⌈L2k⌉),其中 k 是满足2k≥L的最小值,所以H(L)=⌈lgL⌉+1.
所以显然所需空间为
2^H−1=2^(⌈lgL⌉+1)−1
=2×2^(⌈lgL⌉)−1
=2×2(L−1)−1
=4L−5,L≥2
(⌈⌉)为向上取整。