1 条题解
-
0
「线段树覆盖期望」题解
问题分析
我们需要计算:对于线段树的标准建树方式,随机选择一个区间 ,用线段树节点覆盖该区间所需的最少节点数 的期望,乘以区间总数 ,并对 取模。
换句话说,我们需要计算:
其中 表示用线段树节点覆盖区间 所需的最少节点数。
线段树覆盖的性质
1. 线段树的建树方式
对于节点 ,中点 ,左子节点 ,右子节点 。
这是一个标准的线段树建树方式。
2. 的定义
用线段树节点(即树上已有的区间节点)覆盖区间 ,要求:
- 覆盖的节点区间互不相交
- 它们的并集恰好是
- 节点数最少
这实际上就是线段树查询区间 时访问的节点数。
关键性质:在标准线段树中, 等于查询区间 时需要访问的节点数。更具体地说,是线段树区间查询算法中访问的节点数(不包括完全在查询区间外的节点)。
3. 线段树查询的节点数分析
对于查询区间 ,线段树递归查询:
- 如果当前节点区间完全包含于 ,则返回该节点(计数1)
- 否则,递归查询左右子节点
因此, 等于递归查询过程中访问的节点数,其中完全包含于 的节点只计1次。
问题转化
我们需要计算所有区间 的 之和。
设线段树有 个节点(,对于满二叉树,但这里不一定是满二叉树)。
对于每个线段树节点 ,考虑它会对多少查询区间做出贡献。
观察:节点 对查询区间 有贡献(即 包含该节点)当且仅当:
并且该节点在查询过程中被选中(即它是查询分解的一部分)。
但这还不够,因为一个节点被选中还需要满足:它的父节点没有被完全包含在查询区间中。
更精确的分析
1. 节点的贡献条件
对于节点 表示区间 :
- 它被选中当且仅当:
- (完全包含在查询区间内)
- 父节点不被完全包含(否则会选择父节点而不是子节点)
等价地:存在查询区间 使得 被选中的条件是:
- 且
- 且要么 的左边界,要么 的右边界(其中 是父节点区间)
2. 计算每个节点的贡献数
设节点 表示区间 ,长度为 。
父节点 表示区间 ,其中:
- 如果 是左孩子:, 或类似
- 如果 是右孩子:,
实际上,由于建树方式 ,左右子区间长度可能相差1。
关键:节点 被选中当且仅当查询区间 满足:
- 且 (包含 )
- 且不包含整个父节点区间
不包含整个父节点区间意味着:
- 如果 是左孩子:(即 ,因为 已经 )
- 如果 是右孩子:(即 ,因为 已经 )
3. 具体计算
设节点 ,长度为 。
情况1: 是左孩子
- 父节点 ,其中
- 被选中条件:,
- 这样的 对数:
情况2: 是右孩子
- 父节点 ,其中
- 被选中条件:,
- 这样的 对数:
根节点 :总是被选中当查询区间为 时,贡献1。
递归计算
我们可以递归计算所有节点的贡献。
定义 表示区间 对应的节点对总和的贡献。
对于节点 :
-
如果 (叶节点):总是被选中当查询区间为 时
- 贡献:1(只有区间 会选中该叶节点)
-
如果 : 设 左孩子: 右孩子:
节点 本身被选中的条件:查询区间包含 但不包含父节点区间。但对于根节点 ,没有父节点,只有查询区间 会选中它(贡献1)。
对于非根节点,我们需要知道它是左孩子还是右孩子,但这在递归中自然确定。
更好的方法是:在递归过程中,我们知道当前区间 和它的扩展区间(即如果不分裂,父节点会表示的区间)。
另一种思路:直接计算总和
设
我们想找到 的递推公式。
考虑线段树的分裂点
对于区间 ,中点
线段树分裂为:
- 左子树:区间
- 右子树:区间
对于查询区间 :
- 如果 :区间跨越中点,需要分别查询左右子树
- 如果 :区间完全在左子树
- 如果 :区间完全在右子树
递推公式
的递归定义:
- 如果当前节点区间 完全包含于 ,返回1
- 否则,返回
因此,总贡献 可以递归计算。
设 表示区间长度为 时的答案(即所有子区间的 之和)。
考虑根节点 ,中点
将查询区间 分类:
-
完全在左子树:
- 贡献:
-
完全在右子树:
- 贡献:
-
跨越中点:
- 设 ,
- 对于这样的查询,根节点不会被选中(因为区间不完全包含根节点),而是递归查询左右子树
- 贡献:
计算跨越中点的贡献
对于固定的 和 :
$$cover([a,b]) = cover_{\text{左}}(a,m) + cover_{\text{右}}(m+1,b) $$其中 是在左子树中覆盖 的节点数, 类似。
所以跨越中点的总贡献为:
$$\sum_{a=1}^m \sum_{b=m+1}^n [cover_{\text{左}}(a,m) + cover_{\text{右}}(m+1,b)] $$$$= \sum_{a=1}^m \sum_{b=m+1}^n cover_{\text{左}}(a,m) + \sum_{a=1}^m \sum_{b=m+1}^n cover_{\text{右}}(m+1,b) $$$$= (n-m) \cdot \sum_{a=1}^m cover_{\text{左}}(a,m) + m \cdot \sum_{b=m+1}^n cover_{\text{右}}(m+1,b) $$但 不是 !因为 是 ,而这里我们只求和 到 , 固定为 。
实际上, 表示:在左子树中,所有以右端点 结尾的区间的 之和。
类似地, 表示:在右子树中,所有以左端点 开头的区间的 之和。
定义辅助函数
设:
- :所有以 结尾的区间的 之和
- :所有以 开头的区间的 之和
- :所有区间的 之和
由对称性,,因为线段树的结构对称。
递推关系
对于区间 ,中点
- 完全在左:贡献
- 完全在右:贡献
- 跨越中点:
- 对于每个 和 :$$cover([a,b]) = cover_{\text{左}}(a,m) + cover_{\text{右}}(m+1,b) $$
- 总贡献:$\sum_{a=1}^m \sum_{b=m+1}^n [cover_{\text{左}}(a,m) + cover_{\text{右}}(m+1,b)]$
- $= (n-m) \cdot L_{\text{左}}(m) + m \cdot R_{\text{右}}(n-m)$
其中 是在左子树中所有以 结尾的区间的 之和, 是在右子树中所有以 开头的区间的 之和。
由于左右子树结构与原树类似,只是区间范围不同, 函数只依赖于区间长度,所以:
因此跨越中点贡献:
所以:
$$T(n) = T(m) + T(n-m) + (n-m) \cdot L(m) + m \cdot L(n-m) $$还需要 的递推。
的递推
考虑区间 ,中点
对于查询区间 :
- 如果 :区间跨越中点,$cover([a,n]) = cover_{\text{左}}(a,m) + cover_{\text{右}}(m+1,n)$
- 如果 :区间完全在右子树,
所以:
$$L(n) = \sum_{a=1}^m [cover_{\text{左}}(a,m) + cover_{\text{右}}(m+1,n)] + \sum_{a=m+1}^n cover_{\text{右}}(a,n) $$其中:
- $\sum_{a=1}^m cover_{\text{右}}(m+1,n) = m \cdot cover_{\text{右}}(m+1,n)$
- $\sum_{a=m+1}^n cover_{\text{右}}(a,n) = L_{\text{右}}(n-m)$
但 是在右子树中覆盖 的节点数,即 ,而 是整个右子树区间,所以 (整个区间对应一个节点)。
因此:
$$L(n) = L(m) + m \cdot 1 + L(n-m) = L(m) + L(n-m) + m $$总结递推公式
对于 ,设 :
- (只有区间 ,)
对于 :
- (只有区间 ,)
- $T(n) = T(m) + T(n-m) + (n-m) \cdot L(m) + m \cdot L(n-m)$
其中
验证小例子
:
:
- $m = \lfloor (2+1)/2 \rfloor = \lfloor 1.5 \rfloor = 1$
- 检查:区间 : ,: ,总和 ?不对,$L(2)=\sum_{a=1}^2 cover([a,2]) = cover([1,2]) + cover([2,2]) = 1+1=2$,公式给出3,错误。
问题: 在长度为2的线段树中是多少? 线段树:根 ,左 ,右
- :根节点完全包含,所以为1
- :叶节点,为1
所以 ,不是3。
让我们重新推导 。
重新推导
考虑线段树根节点 ,中点
对于 的取值:
情况1:
- 查询区间 跨越中点
- $cover([a,n]) = cover_{\text{左}}(a,m) + cover_{\text{右}}(m+1,n)$
- :由于 是整个右子树区间,所以等于1
所以对于 :
情况2:
- 查询区间 完全在右子树
因此:
$$L(n) = \sum_{a=1}^m [cover_{\text{左}}(a,m) + 1] + \sum_{a=m+1}^n cover_{\text{右}}(a,n) $$$$= \sum_{a=1}^m cover_{\text{左}}(a,m) + m + \sum_{a=m+1}^n cover_{\text{右}}(a,n) $$现在:
- $\sum_{a=1}^m cover_{\text{左}}(a,m) = L_{\text{左}}(m) = L(m)$
- $\sum_{a=m+1}^n cover_{\text{右}}(a,n) = L_{\text{右}}(n-m) = L(n-m)$
所以:
但验证 :
- 但实际
矛盾。问题在于:对于 ,(根节点),但公式给出 $cover_{\text{左}}(1,1) + 1 = cover([1,1]) + 1 = 1+1=2$,错误。
错误原因:当 时,区间 完全包含根节点,所以 ,不应该递归到左右子树。
所以需要修正:当 时,区间 完全包含根节点,,不递归。
修正递推
对于 :
特殊情况: 时,
对于 :
- 如果 :$cover([a,n]) = cover_{\text{左}}(a,m) + cover_{\text{右}}(m+1,n)$ 由于 是整个右子树, 所以
- 如果 :
因此:
$$L(n) = 1 + \sum_{a=2}^m [cover_{\text{左}}(a,m) + 1] + \sum_{a=m+1}^n cover_{\text{右}}(a,n) $$$$= 1 + \sum_{a=2}^m cover_{\text{左}}(a,m) + (m-1) + \sum_{a=m+1}^n cover_{\text{右}}(a,n) $$现在:
- $\sum_{a=2}^m cover_{\text{左}}(a,m) = L(m) - cover_{\text{左}}(1,m)$ 但 (在左子树中,区间 是整个区间) 所以
因此:
$$L(n) = 1 + (L(m)-1) + (m-1) + L(n-m) = L(m) + L(n-m) + m - 1 $$验证 :
- ,正确。
验证 :
- 检查:区间 : ,: ,: ,总和 ,正确。
的修正递推
考虑根节点 ,中点
分类:
- 区间完全在左:,贡献
- 区间完全在右:,贡献
- 区间跨越中点:
对于跨越中点的区间 :
- 如果 且 :
- 否则:$cover([a,b]) = cover_{\text{左}}(a,m) + cover_{\text{右}}(m+1,b)$
所以跨越中点贡献:
$$\text{贡献} = 1 + \sum_{a=1}^m \sum_{b=m+1}^n [cover_{\text{左}}(a,m) + cover_{\text{右}}(m+1,b)] - [cover_{\text{左}}(1,m) + cover_{\text{右}}(m+1,n)] $$最后减去的是 的情况,因为我们已经单独计算了 。
但 ,,所以:
$$\text{跨越中点贡献} = 1 + \sum_{a=1}^m \sum_{b=m+1}^n [cover_{\text{左}}(a,m) + cover_{\text{右}}(m+1,b)] - 2 $$计算:
$$\sum_{a=1}^m \sum_{b=m+1}^n cover_{\text{左}}(a,m) = (n-m) \cdot \sum_{a=1}^m cover_{\text{左}}(a,m) = (n-m) \cdot L(m) $$$$\sum_{a=1}^m \sum_{b=m+1}^n cover_{\text{右}}(m+1,b) = m \cdot \sum_{b=m+1}^n cover_{\text{右}}(m+1,b) $$但
所以:
$$\text{跨越中点贡献} = 1 + (n-m) \cdot L(m) + m \cdot L(n-m) - 2 $$因此:
$$T(n) = T(m) + T(n-m) + (n-m) \cdot L(m) + m \cdot L(n-m) - 1 $$验证 :
- $T(2) = T(1) + T(1) + (2-1)\cdot L(1) + 1\cdot L(1) - 1 = 1+1+1\cdot1+1\cdot1-1 = 1+1+1+1-1=3$ 检查:区间 :1,:1,:1,总和3,正确。
验证 :
- $T(3) = T(2) + T(1) + (3-2)\cdot L(2) + 2\cdot L(1) - 1 = 3+1+1\cdot2+2\cdot1-1=3+1+2+2-1=7$ 与样例一致。
最终算法
我们需要计算:
递推公式: 对于 ,设
-
-
$$T(n) = T(m) + T(n-m) + (n-m) \cdot L(m) + m \cdot L(n-m) - 1 $$
由于 可达 ,不能直接递归计算。但注意到 和 大约是 ,递归深度为 。
我们可以用记忆化搜索(递归+哈希表)计算,状态数是 级别。
模运算
所有计算在模 下进行。
由于 很大,计算 时需要先取模。
注意: 本身需要取模 吗?在公式中 出现在 和 中,这些是系数,需要取模。
但 也需要在模意义下计算吗? 是整数除法,可以在普通整数下计算,然后取模。
由于 很大,我们需要处理大整数运算。
复杂度分析
- 递归深度:
- 状态数:(每个不同的 值)
- 每次计算
- 总复杂度:,对于 ,,完全可行。
总结
关键步骤:
- 定义 和 的递推关系
- 通过分析线段树查询过程得到递推公式
- 使用记忆化搜索高效计算
- 注意模运算和大整数处理
递推公式:
- $L(n) = L(\lfloor \frac{n+1}{2} \rfloor) + L(n - \lfloor \frac{n+1}{2} \rfloor) + \lfloor \frac{n+1}{2} \rfloor - 1$
- $T(n) = T(\lfloor \frac{n+1}{2} \rfloor) + T(n - \lfloor \frac{n+1}{2} \rfloor) + (n - \lfloor \frac{n+1}{2} \rfloor) \cdot L(\lfloor \frac{n+1}{2} \rfloor) + \lfloor \frac{n+1}{2} \rfloor \cdot L(n - \lfloor \frac{n+1}{2} \rfloor) - 1$
所有运算对 取模。
- 1
信息
- ID
- 5909
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者