线段树杂题

影魔

问题描述
给定一个 1→n1 \to n 的排列 {a1,a2,⋯an}\{a_1, a_2, \cdots a_n\}
当存在 i<ji < j 并且 [i+1,j−1][i+1, j-1] 中所有数都满足 ⩽min⁡{ai,aj}\leqslant \min \{a_i, a_j\},影魔会获得 p1p_1 的攻击力
特别地,(i,i+1)(i, i+1) 这样一对数总会给影魔提供 p1p_1 的攻击力
另外,令 cc 为 max⁡{ai+1,ai+2,⋯aj−1}\max\{a_{i+1}, a_{i+2}, \cdots a_{j-1}\},当 ai<c<aja_i < c < a_j 或者 aj<c<aia_j < c < a_i
影魔会获得 p2p_2 的攻击力

现在给出 mm 个问询,对于每个问询,求 [ql,qr][ql, qr] 区间内的灵魂能为影魔提供多少攻击力

算法分析
注意到问题给出的是关于 a[1⋯n]a[1\cdots n] 的一个排列,也就是说所有元素都不相同
不难想到,对于某个 ii,考虑它会对哪些位置或者区间产生贡献

  • [i,i+1][i, i+1] 肯定会产生贡献 p1p_1,这部分贡献相对独立,对于每个问询 [ql,qr][ql, qr],可以单独计算
    这部分的贡献就是 p1⋅(qr−ql)p_1 \cdot (qr-ql),是相对固定的
  • 可以想到找到左边第一个比 ii 大的数 l(i)l(i),和右边第一个比 ii 大的数 r(i)r(i)
    那么 [l(i)+1,r(i)−1][l(i)+1, r(i)-1] 中的数都 <min⁡{l(i),r(i)}< \min\{l(i), r(i)\},影魔在 l(i)l(i) 处获得 p1p_1 攻击力
    这可以用线段树执行单点增加,l(i)l(i) 增加 p1p_1 即可维护
    至于 l(i),r(i)l(i), r(i),可以用单调栈预处理
  • 另外,a(li+1,li+2,⋯i−1)<a(i)<a(ri)a(l_i+1, l_i+2, \cdots i-1) < a(i) < a(r_i),此时 aia_i 产生的贡献是多少?
    每一个 ∀j∈[li+1⋯i−1]\forall j \in [l_i+1\cdots i-1] 都会有 p2p_2 的贡献,用线段树区间增加维护
    执行 [li+1,i−1][l_i + 1, i-1] 的区间增加,注意维护增加的值为 p2⋅lenp_2 \cdot len,lenlen 为区间长度
    同理,a(i+1,i+2,⋯ ,ri−1)<a(i)<a(li)a(i+1, i+2, \cdots, r_i-1) < a(i) < a(l_i),执行区间 [i+1,ri−1][i+1, r_i-1] 增加 p2p_2

每一个位置 ii 都可以用以上方法维护贡献,如果我们要求得影魔获得的总攻击力
只需要在线段树上查询 [1,n][1, n] 即可,比较棘手的是处理问询子区间 [ql,qr][ql, qr]

假设现在考虑询问 idid
对于位置 i∉[ql,qr]i \notin [ql, qr],按之前的方法维护出 ii 在线段树上整体的贡献
再进一步查询得到对 [ql,qr][ql, qr] 的贡献,这部分贡献是要扣除的
想到这里,可以考虑主席树的思想
[ql,qr][ql, qr] 的贡献等于 qrqr 处添加的贡献减去 ql−1ql-1 处添加的贡献,具体来说
对于 qrqr 版本的线段树,查询 [ql,qr][ql, qr],记为 S(qr)S(qr)
对于 ql−1ql-1 版本的线段树,查询 [ql,qr][ql, qr],记为 S(ql−1)S(ql-1),那么答案为 S(qr)−S(ql−1)S(qr)-S(ql-1)

可以对所有问询离线,然后扫描 i∈[1,n]i \in [1, n]
如果某个 ii 和某个问询的 ql−1ql-1 重合,此时线段树上正好维护了 [1⋯ql−1][1\cdots ql-1] 产生的总贡献
此时问询 [ql,qr][ql, qr] 就对应 i<qli < ql 的点,对 [ql,qr][ql, qr] 的贡献,这部分贡献是要扣除的,记为 S2(id)S_2(id)
如果 ii 和问询的 qrqr 重合,此时线段树包括了从 [1→qr][1\to qr] 所有的 ii 产生的贡献
查询区间 [ql,qr][ql, qr] 得到 S1(id)S_1(id),令 S1(id)−S2(id)S_1(id) - S_2(id) 就是答案

CF 757

EDU 118

D.MEX Sequences
题目大意
给定序列 a1,a2,⋯ ,ana_1, a_2, \cdots, a_n,问存在多少个 mex-correct 子序列
其中子序列满足 (ai1,ai2,⋯aim)(a_{i1}, a_{i2}, \cdots a_{im}),1⩽i1<i2<⋯<im⩽n1 \leqslant i1 < i2 < \cdots < im \leqslant n
只要下标不同就算不同的子序列,如果两个序列取相同的值,但是下标不同,就算不同的序列

mex-correct 子序列 (x1,x2,⋯xk)(x_1, x_2, \cdots x_k) 定义如下
对于每一个元素 i∈[1,k]i \in [1, k],前缀 mex 值为 mex[i]=mex(x1,x2,⋯xi)\textbf{mex}[i] = \bm{mex}(x_1, x_2, \cdots x_i)
每个元素都必须满足 ∣xi−mex[i]∣⩽1|x_i - \text{mex}[i]| \leqslant 1

算法设计

考虑 dp\bm{dp},对于每一个位置 ii,如果 ai=xa_i = x,不难发现只有以下 33 种情况

  • [1⋯i−1][1\cdots i-1] 包含所有 {0⋯x−2}\{0\cdots x-2\} 的元素,mex(i−1)=x−1\textbf{mex}(i-1) = x-1

    • 如果选择 aia_i,存在转移
      [1⋯i][1\cdots i] 包含所有 {0⋯x−2}∪{x}\{0 \cdots x-2\} \cup \{x\} 的元素,mex(i)=x−1\textbf{mex}(i) = x-1
    • 不选 aia_i,存在转移
      [1⋯i][1\cdots i] 包含所有 {0⋯x−2}\{0 \cdots x-2\} 的元素,并且 mex(i)=x−1\textbf{mex}(i) = x-1
  • [1⋯i−1][1\cdots i-1] 包含所有 {0⋯x−1}\{0\cdots x-1\} 的元素, mex(i−1)=x\textbf{mex}(i-1) = x

    • 如果选择 aia_i,存在转移
      [1⋯i][1\cdots i] 包含所有 {0⋯x}\{0 \cdots x\} 的元素,mex(i)=x+1\textbf{mex}(i) = x+1
    • 不选 aia_i,存在转移
      [1⋯i][1\cdots i] 包含所有 {0⋯x−1}\{0 \cdots x-1\} 的元素,mex(i)=x\textbf{mex}(i) = x
  • [1⋯i−1][1\cdots i-1] 包含所有 {0⋯x}\{0\cdots x\} 的元素,mex(i−1)=x+1\textbf{mex}(i-1) = x+1

    • 如果选择 aia_i,存在转移
      [1⋯i][1\cdots i] 包含所有 {0⋯x}\{0 \cdots x\} 的元素,mex(i)=x+1\textbf{mex}(i) = x+1
    • 不选 aia_i,存在转移
      [1⋯i][1\cdots i] 包含所有 {0⋯x}\{0 \cdots x\} 的元素,mex(i)=x+1\textbf{mex}(i) = x+1

注意到后两种情况,都是 dp(i−1,x)→dp(i,x)dp(i-1, x) \to dp(i, x) 或者 dp(i−1,x)→dp(i,x+1)dp(i-1, x) \to dp(i, x+1) 的转移
可以考虑递推,具体来说,用 dp(i,x)dp(i, x) 表示状态 mex(i)=x\textbf{mex}(i) = x 对应的方案数

  • 对于第二种情况,dp(i−1,x)→{dp(i,x)dp(i,x+1)dp(i-1, x) \to \displaystyle\begin{cases}dp(i, x) \\ dp(i, x+1)\end{cases}

  • 对于第三种情况,dp(i−1,x+1)→dp(i,x+1)dp(i-1, x+1) \to dp(i, x+1)

那么对于位置 ii,合法的方案数为 dp(i,x)+dp(i,x+1)dp(i, x) + dp(i, x+1),这部分累加到答案中
从 i→i+1i \to i+1 递推的时候,存在转移 dp(i+1,x+1)←dp(i,x)+dp(i,x+1)dp(i+1, x+1) \leftarrow dp(i, x) + dp(i, x+1)
算法实现时候,可以省略第一维,用 dp(x+1)+=(dp(x)+dp(x+1))dp(x+1) += (dp(x) + dp(x+1))

下面单独处理第一种情况
注意到对于第一种情况,首先有 dp(i,x−1)←dp(i−1,x−1)dp(i, x-1) \leftarrow dp(i-1, x-1)
还有很重要的一点是,对于 [i+1⋯n][i+1\cdots n] 能选的数是固定的,只能是 {x−2,x}\{x-2, x\} 两种
只要一次预处理,算出 suf(i)\text{suf}(i) 表示后缀 [i+1⋯n][i+1\cdots n] 中有几个 {x−2,x}\{x-2, x\} 即可
此时的方案数为 dp(i,x−1)⋅2suf(i)dp(i, x-1) \cdot 2^{\text{suf}(i)}
power(2,suf(i))\textbf{power}(2, \text{suf}(i)) 就是后缀可选元素的子集个数