单侧递归线段树

一侧儿子会影响另一侧儿子的信息

例 1:P4198 楼房重建

\(a_{1\sim n}\)\(m\) 次操作每次修改其中一个位置,每次操作后输出此时 \(\frac{a_i}{i}\) 的前缀最大值数量,\(n,m\le10^5\)

单侧递归线段树模板题

\(b_i=\frac{a_i}i\) 建立线段树,相当于单点修改或查询全局前缀最大值数量

\(mx[L:R]=\max_{i=L}^R b_i\)(保存在线段树的结点 \([L:R]\) 上,下同),\(res[L:R]\) 为区间 \([L:R]\) 的前缀最大值数量

定义 \(Q(v,l,r)\)\(b[l:r]\) 中大于 \(v\) 的前缀最大值数量

\(M\) 为区间 \([l:r]\) 的中点,则

\[Q(v,l,r)=\begin{cases} 0&mx[l:r]\le v\\ 1&mx[l:r]>v,l=r\\ Q(v,M+1,r)&mx[l:r]>v,l\ne r,mx[l:M]\le v\\ Q(v,l,M)+res[l:r]-res[l:M]&\text{otherwise} \end{cases}\]

于是可以 \(O(\log n)\) 求出一个 \(Q(v,l,r)\)

单点修改 \(\text{push\_up}\) 的过程中,\(res[l:r]=res[l:M]+Q(mx[l:M],M+1,r)\)\(mx\) 的跟新是容易的

因此单点修改单次 \(O(\log^2 n)\)

总时间复杂度 \(O(m\log^2n)\)

代码

例 2:QOJ [PKUWC 2024 Day 2] # 8229. 栈

\(n\) 个栈,\(q\) 次操作,向 \([l,r]\) 中的栈分别压入 \(x\)\(y\)\([l,r]\) 中的栈分别弹栈 \(x\) 次(弹栈次数可能超过此时元素数,此时视为清空栈),查询第 \(p\) 个栈中从下往上第 \(l\) 个到第 \(r\) 个元素的和,\(n,q\le10^5,x,y\le10^9\)

离线操作,对栈进行扫描线,转化为给定数组 \(v_{1\sim m}\),有数组 \(a_{1\sim m}\),若干次操作为 \(a\) 的单点修改,或给定 \(ct\)\(R\) 查询 \(\sum_{i=1}^R v_i \max(0,\min(\text{tmp}(i,R),ct-\sum_{j=1}^{i-1} \text{tmp}(j,R)))\),其中 \(\text{tmp}(i,R)\)\(a[i:R]\) 的最小不可空前缀和与 \(0\)\(\max\),表示此时第 \(i\) 个操作到第 \(R\) 个操作时能剩下的数量

以下用 \(ar[L:R]\) 表示保存在线段树 \([L:R]\) 区间上的 \(ar\) 数组值,令 \(M\) 为区间中点,左右子区间为 \([L,M]\)\((M,R]\)

\(sm[L:R]\)\(a\) 的区间和,\(mn[L:R]\) 为区间最小可空前缀和,\(mx[L:R]\) 为区间最大可空后缀和

\(tt[L:R]=\sum_{i=L}^R v_i tmp(i,R)\),令 \(Lt[L:R]=tt[L:R]-tt[M+1:R]\)

定义函数 \(\text{Dv}(ad,L,R)\),其中 \([L:R]\) 为线段树的一个结点,\(ad\ge 0\),令 \(\text{Dv}(ad,L,R)=\sum_{i=L}^R v_i \max(0,\min(\text{tmp}(i,R),\sum_{j=i}^R \text{tmp}(j,R)-ad))\)

定义函数 \(\text{gtmn}(L,R)\) 返回一个二元组,其中 \([L:R]\) 不一定为线段树上的结点,返回值的第一个数意义同 \(mn\),第二个数意义同 \(sm\)

定义函数 \(\text{qry}(ct,R,ad,l,r)\),其中 \(ad=\text{gtmn}(r+1,R)[mn]\)(默认空区间的 \(mn\)\(0\)),\([l:r]\) 为线段树上的区间,\(ad\le 0\),令 \(\text{qry}(ct,R,ad,l,r)=\sum_{i=l}^{\min(r,R)} v_i\max(0,\min(\text{tmp}(i,R),ct-\sum_{j=l}^{i-1}\text{tmp}(j,R)))\)\(ad\) 用于辅助计算

实现 \(\text{push\_up}\) 后容易实现 \(a\) 的单点加,容易由 \(\text{qry}\) 得到询问的式子的值

考虑如何实现上述函数和维护数组

\(sm,mn,mx\)\(\text{push\_up}\) 时容易 \(O(1)\) 维护,\(\text{gtmn}\) 容易做到单次 \(O(\log n)\)

有以下等式:

  • \(mn[L:R]+mx[L:R]=sm[L:R]\)
  • \(mx[L:R]=\sum_{j=L}^R \text{tmp}(j,R)\)\(L\) 到前缀和最小值处的 \(\text{tmp}\) 都是 \(0\),设剩余部分中选出若干位置 \(p_{1\sim t}\),满足这些位置上的 \(a\) 的前缀和递增,且 \((p_i,p_{i+1})\) 内的 \(a\) 的前缀和都大于 \(p_i\)\(a\) 的前缀和,这样 \(p_i\) 处的 \(\text{tmp}\) 等于 \([p_i,p_{i+1})\) 的区间和,\((p_i,p_{i+1})\) 内的 \(\text{tmp}\) 都是 \(0\),即后缀最大值部分的每个 \(a_i\) 恰好计入总和一次)

先考虑如何实现 \(\text{Dv}(ad,L,R)\)

  • \(ad=0\) 时直接返回 \(tt[L:R]\)
  • \(L=R\) 时返回 \(v_L \max(0,\text{tmp}(L,R)-ad)\)
  • \(mx[M+1:R]=\text{tmp}(j,R)\ge ad\),则返回 \(\text{Dv}(ad,M+1,R)+Lt[L:R]\)
  • 否则 \([M+1:R]\) 内的 \(tmp\) 都是 \(0\),同时 \([M+1:R]\)\(ad\) 抵消的值为 \(sm[M+1:R]\),返回 \(\text{Dv}(ad-sm[M+1:R],L,M)\)

显然可以 \(O(\log n)\) 计算一个 \(\text{Dv}\)

然后考虑实现 \(\text{push\_up}\),此时只需要考虑 \(Lt\) 的维护,其他值的维护都是容易的

  • \(mn[M+1:R]=0\) 时,右侧对左侧没有影响,此时 \(Lt[L:R]=tt[L:M]\)
  • 否则 \(Lt[L:R]=\text{Dv}(-mn[M+1:R],L,M)\)

最后考虑实现 \(\text{qry}(ct,R,ad,l,r)\)

  • \(ct=0\) 时显然返回 \(0\)
  • \(l=r\) 时返回 \(v_l\max(0,\min(ct,sm[l:r]+ad))\)(假定 \(l\le R\)
  • \(R\le M\) 时返回 \(\text{qry}(ct,R,ad,l,M)\)
  • 以上都是显然的
  • 若不满足,令 \(mn=\text{gtmn}(1,R)[mn],sm=\text{gtmn}(M+1,\min(r,R))[sm]\),令 \(Rt=\min(mn,sm+ad)\) 为区间 \([M+1,R]\)\(mn\)
  • \(ct<Rt+mx[l:M]\),则右区间对答案没有贡献,返回 \(\text{qry}(ct,R,Rt,l,M)\)
  • 否则返回 \(\text{Dv}(-Rt,l,M)+\text{qry}(ct-\max(0,mx[l:M]+Rt),R,ad,M+1,r)\)

总时间复杂度 \(O(n\log^2n)\)(假设 \(n,q\) 同阶)

代码

参考

  1. \(\text{2024.12.13 EZDS.pdf\;\;\; by Luzhuoyuan}\)
posted @ 2025-05-05 21:25  Hstry  阅读(28)  评论(0)    收藏  举报